/* ============================================================================
 * INSPIRE TWDC  —  self-hosted webfont family
 * ----------------------------------------------------------------------------
 * Before this block the deck named each face as its own installed family
 * ("InspireTWDC Heavy", "InspireTWDC Black", ...) and asked for
 * font-weight: 900. Each OTF registers with nameID 1 = "InspireTWDC <Face>"
 * and nameID 2 = "Regular", so those families expose exactly one Regular
 * member. Requesting 900 from them had two effects, both wrong:
 *
 *   1. The browser had no 900 face to select, so it SYNTHESIZED bold on top
 *      of a face that is already heavy. That is why deck subtitles read far
 *      heavier than the Figma source.
 *   2. Any face other than Black failed to resolve at all on machines where
 *      only some OTFs are installed, silently falling through to
 *      MultiplaneTWDC Display or a generic sans.
 *
 * The family is now declared once, self-hosted, with font-weight values
 * taken from each file's real OS/2 usWeightClass:
 *
 *   InspireTWDC Light   usWeightClass 300  ->  300
 *   InspireTWDC Roman   usWeightClass 400  ->  400   (+ Italic)
 *   InspireTWDC Medium  usWeightClass 500  ->  500
 *   InspireTWDC Heavy   usWeightClass 600  ->  600
 *   InspireTWDC Black   usWeightClass 800  ->  800
 *
 * This family has no Bold (700) and no Semibold. Heavy (600) and Black (800)
 * are the only weights above Medium, so a request for 700 resolves to Black.
 * font-synthesis: none on the deck keeps the browser from inventing weights.
 * ========================================================================== */
@font-face {
  font-family: "InspireTWDC";
  src: url("./assets/fonts/InspireTWDC-Light.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "InspireTWDC";
  src: url("./assets/fonts/InspireTWDC-Roman.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "InspireTWDC";
  src: url("./assets/fonts/InspireTWDC-Italic.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "InspireTWDC";
  src: url("./assets/fonts/InspireTWDC-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "InspireTWDC";
  src: url("./assets/fonts/InspireTWDC-Heavy.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "InspireTWDC";
  src: url("./assets/fonts/InspireTWDC-Black.woff2") format("woff2");
  font-weight: 800;
  font-style: normal;
  font-display: swap;
}

/* Legacy per-face family aliases. Deck rules authored before the family was
 * self-hosted still name these; each one maps to the single real face at its
 * true weight so nothing synthesizes and nothing falls back. */
@font-face {
  font-family: "InspireTWDC Light";
  src: url("./assets/fonts/InspireTWDC-Light.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "InspireTWDC Roman";
  src: url("./assets/fonts/InspireTWDC-Roman.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "InspireTWDC Medium";
  src: url("./assets/fonts/InspireTWDC-Medium.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "InspireTWDC Heavy";
  src: url("./assets/fonts/InspireTWDC-Heavy.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "InspireTWDC Black";
  src: url("./assets/fonts/InspireTWDC-Black.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* ============================================================================
 * Rate Card Manager — EDL component library (Figma source of truth)
 * All tokens pulled from the EDL Figma variable set:
 *   Indigo/5  = #F3F3FF   Indigo/10 = #E6E6FF
 *   Indigo/15 = #CFCFFF   Indigo/30 = #928AFF   Indigo/70 = #3611C8
 *   Gray/05 = #F7F9FA   Gray/20 = #D4DEE3   Gray/30 = #B6C4D3
 *   Gray/40 = #9AABBB   Gray/50 = #8496A9   Gray/70 = #526374   Gray/95 = #171C20
 * Typography (T-scale):
 *   T60 28/38 weight 500 (page title)
 *   T20 14/21 weight 400 (body)
 *   T15 14/16 weight 500 (UI label)
 *   T10 12/18 weight 400 (small)
 * ========================================================================== */

:root {
  /* EDL Indigo */
  --indigo-70: #3611C8;
  --indigo-30: #928AFF;
  --indigo-15: #CFCFFF;
  --indigo-10: #E6E6FF;
  --indigo-5:  #F3F3FF;

  /* Shared placeholder color used by EVERY field family (input,
   * select, date picker, search, textarea, fake-field). 0F1214 @ 30%
   * per the latest design call - keeps placeholders distinct from
   * filled values across both EDL and ADS themes. */
  --placeholder-color: rgba(15, 18, 20, 0.30);

  /* EDL Gray */
  --gray-95: #171C20;
  --gray-70: #526374;
  --gray-50: #8496A9;
  --gray-40: #9AABBB;
  --gray-30: #B6C4D3;
  --gray-20: #D4DEE3;
  --gray-10: #EAEEF2; /* derived: row divider — between Gray/20 and Gray/05 */
  --gray-05: #F7F9FA;

  /* Surfaces.
   * --page-bg is the color of <body> - the canvas behind everything else.
   * Set it to Gray-05 to match the Figma .page__content band so the page
   * background stays continuous when content is shorter than the viewport
   * (no white block leaks below the table card). Components that need
   * white (the .page__top header band, the .surface card) specify
   * #FFFFFF directly and are unaffected. */
  --page-canvas-background: #F3F3F3; /* Figma 474:18796 page canvas */
  --page-bg: #F7F9FA;        /* Gray-05, matches .page__content band */
  --surface: #FFFFFF;
  --app-safe-area-top: env(safe-area-inset-top, 0px);
  --app-safe-area-right: env(safe-area-inset-right, 0px);
  --app-safe-area-bottom: env(safe-area-inset-bottom, 0px);
  --app-safe-area-left: env(safe-area-inset-left, 0px);
  --app-header-height: 56px;
  --app-header-block-size: calc(
    var(--app-header-height) + var(--app-safe-area-top)
  );

  /* Nav — Figma uses EDL Indigo/70 for the global nav background. */
  --nav-bg: #3611C8;
  --nav-text: #FFFFFF;

  /* Danger (delete) */
  --danger-bg: #FDECEC;
  --danger-text: #B42318;
  --danger-btn: #D92D20;
  --danger-btn-hover: #B42318;
  --scrim: rgba(20, 22, 38, 0.45);

  /* ===== Table typography tokens (EDL Regular type ramp) =====================
   * T10  12/18 Roman    — small / chip / pagination meta
   * T15  14/16 Medium   — UI control labels (button text)
   * T20  14/21 Roman    — body / table cells / links / pagination main
   * Header labels render at T10 12/18 Gray/70 per EDL data-table component.
   */
  --table-toolbar-text:    14px/16px "InspireTWDC", "DM Sans", sans-serif;  /* T15 */
  --table-toolbar-weight:  500;
  --table-toolbar-color:   var(--indigo-70);

  --table-header-text:     12px/18px "InspireTWDC", "DM Sans", sans-serif;  /* T10 */
  --table-header-weight:   400;
  --table-header-color:    var(--gray-70);

  --table-body-text:       14px/21px "InspireTWDC", "DM Sans", sans-serif;  /* T20 */
  --table-body-weight:     400;
  --table-body-color:      var(--gray-95);

  --table-link-text:       14px/21px "InspireTWDC", "DM Sans", sans-serif;  /* T20 */
  --table-link-weight:     400;
  --table-link-color:      var(--indigo-70);

  --table-chip-text:       12px/18px "InspireTWDC", "DM Sans", sans-serif;  /* T10 */
  --table-chip-text-size:  12px;
  --table-chip-weight:     400;

  --table-pagination-text: 14px/21px "InspireTWDC", "DM Sans", sans-serif;  /* T20 */
  --table-pagination-color:var(--gray-95);

  --table-control-text:    14px/21px "InspireTWDC", "DM Sans", sans-serif;  /* T20 */
  --table-control-color:   var(--gray-95);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--page-bg);
  color: var(--gray-95);
  /* Force the body to fill at least the viewport so the page-bg
   * (Gray-05 / #F7F9FA) shows from the bottom of the table card
   * down to the bottom of the viewport. Without this, when the
   * viewport is taller than the natural content height, there used
   * to be a visible WHITE band between the bottom of .page__content
   * and the viewport bottom on certain ancestor stacks (Edit /
   * Create pages especially). 100vh on html+body locks it gray. */
  min-height: 100vh;
  min-height: 100dvh;
  /* ADS body font is Open Sans (font/body/normal/md token). Open Sans
   * loads via the Google Fonts link in index.html. "InspireTWDC" is
   * Disney's private brand display face - kept as the first fallback
   * for any future deployment that ships the licensed file; "DM Sans"
   * is the closest public stand-in if neither is available. Putting
   * Open Sans first ensures the body, table, chips, buttons, search,
   * and pagination render at the exact lighter weight the Figma ADS
   * tokens specify, instead of the heavier DM Sans face we were
   * accidentally rendering before Open Sans was added to the Google
   * Fonts link. */
  font-family: "Open Sans", "InspireTWDC", "DM Sans", -apple-system,
    BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  font-size: 14px;
  line-height: 21px; /* T20 */
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* The product page canvas. Presentation surfaces are excluded so the
 * shared presentation canvas is never briefly painted over during a
 * transition: the atlas route by its data-route, the layer explainer by
 * the body class it sets while its overlay owns the screen. */
body:not([data-route="create"]):not([data-route="line"]):not([data-route="atlas"]):not(.rcle-open) {
  background: var(--page-canvas-background);
}

button, input, select { font-family: inherit; }

/* ============================== GLOBAL NAV ============================
 * Spec from Figma node 139-3872 (Rate Card "Navigation" frame).
 *   Bar:      h=56, bg Indigo/70 #3611C8, full-bleed.
 *   Brand:    x=24, y=14, w=156 h=28 (Disney Advertising wordmark).
 *   Nav tabs: x=566, y=10, gap=4, h=36; TopBarTab variant
 *             - Inactive: Open Sans 14/21 #E6E6FF, padding 8/12, no chevron.
 *             - Active (Admin): bold white text + 2px white underline bar.
 *   Utility:  bell (20x20), apps (DotsThree 20x20), avatar (32x32 circle),
 *             gap 16, right padding 24, vertically centered.
 *   Color:    Indigo/10 #E6E6FF for icons + inactive text; white for active.
 */
/* Atlas navigation tokens (Figma 184:4469).
 *   --navigation/topbar/background = indigo/300 (#4045C2)
 *   --navigation/topbar/text       = #FFFFFF
 *   --navigation/size/.../padding-x = 24px
 *   --navigation/size/gap          = 8px  (brand-icon to brand-name)
 *   --navigation/size/nav-tabs/gap = 4px  (between TopBarTab items)
 *   --navigation/size/utilities/gap = 16px (between right-side icons)
 *   --font/body/normal/md/font-size = 14px  (inactive + active label)
 *   --font/body/normal/md/line-height = 20px
 *   --font/body/normal/xl/font-size = 18px  (Atlas wordmark)
 *   --font/body/normal/xl/line-height = 24px
 * These tokens are declared inline (no extra :root block) at the
 * .gnav rule so they ship with the nav and don't leak into the rest
 * of the app. */
.gnav {
  --navigation-topbar-background: #4045C2;
  --navigation-topbar-text:       #FFFFFF;
  --navigation-padding-x:         24px;
  --navigation-gap:               8px;
  --navigation-nav-tabs-gap:      4px;
  --navigation-utilities-gap:     16px;
  --font-body-normal-md-font-size:  14px;
  --font-body-normal-md-line-height:20px;
  --font-body-normal-xl-font-size:  18px;
  --font-body-normal-xl-line-height:24px;
  /* Sticky top bar (2026-07-09 sticky-header brief). The .gnav pins to
   * the top of the viewport so that when the Rate Card / Pricing page
   * scrolls, the horizontal top nav stays visible instead of scrolling
   * away and leaving the side rail (.vnav, fixed at top: 56px) floating
   * over a bare white gap. Notes:
   *   - `top: 0` sits it flush against the browser chrome.
   *   - `z-index: 45` sits above the side rail (.vnav @ 40) and page
   *     content, and comfortably below the modal scrim (@ 50+) and the
   *     Quick Edit sheet (@ 1300), so no overlay ever gets covered.
   *   - Sticky works only because html/body use `overflow-x: clip`
   *     (see the "Page shell guard" block); with `overflow-x: hidden`
   *     the effective overflow-y becomes `auto` and body becomes a
   *     scroll container that would trap the sticky.
   *   - Background is the indigo topbar token (opaque) so scrolled
   *     content behind never bleeds through.
   *   - Atlas deck route hides .gnav entirely (see body[data-route=
   *     "atlas"] rules), so this sticky behavior is inert on the
   *     presentation deck. */
  position: sticky;
  top: 0;
  z-index: 45;
  /* Per Figma 214:10213 the top bar is a three-anchor layout:
   *   brand     left  (justify-self: start)
   *   nav       CENTER (justify-self: center) - centered in the
   *             viewport regardless of brand or utilities width,
   *             matching the ADS header spec
   *   utilities right (justify-self: end)
   *
   * CSS grid with 1fr/auto/1fr makes columns 1 and 3 absorb equal
   * slack, putting column 2 (the nav) at the visual center of the
   * header. When v1.1 hides .gnav__items via display:none, column 2
   * collapses to 0 width but columns 1 and 3 still resolve to equal
   * widths from the 1fr tracks, so the right cluster stays at the
   * right edge - the failure mode that prompted the earlier
   * flex-based fix only happened because the previous nav <ul> was
   * EMPTY but present in DOM; now it's either populated (v1.0) or
   * display:none (v1.1), both of which the grid handles cleanly. */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  height: var(--app-header-block-size);
  padding:
    var(--app-safe-area-top)
    max(var(--navigation-padding-x), var(--app-safe-area-right))
    0
    max(var(--navigation-padding-x), var(--app-safe-area-left));
  background: var(--navigation-topbar-background);
  color: var(--navigation-topbar-text);
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
}
.gnav__left {
  display: flex;
  align-items: center;
  height: 100%;
  justify-self: start;
  /* Explicit column assignment so the brand always occupies grid
   * column 1 even if the middle .gnav__items <ul> is display:none
   * in v1.1 (without this, auto-placement collapses items into
   * preceding tracks and the right cluster ends up in col 2). */
  grid-column: 1;
}
.gnav__right {
  display: flex;
  align-items: center;
  height: 100%;
  gap: var(--navigation-utilities-gap, 16px);
  justify-self: end;
  /* Pin to column 3 unconditionally - see note on .gnav__left. */
  grid-column: 3;
}
.gnav__items {
  grid-column: 2;
}
.gnav__module {
  display: flex;
  align-items: center;
  gap: 0;
  height: 100%;
  padding: 0;
}

/* Brand cluster - Figma 184:4470 reserves a 28x28 brand-icon + 8px gap
 * + "Atlas" wordmark at 18/24 Open Sans Bold. The legacy Disney+Ad
 * Platforms wordmark SVG is kept hidden in DOM for QA selectors but
 * occupies zero space (hidden attribute + the rule below). */
.gnav__logomark {
  display: inline-flex;
  align-items: center;
  gap: var(--navigation-gap, 8px);
  height: 28px;
  text-decoration: none;
  color: var(--navigation-topbar-text, #FFFFFF);
  flex-shrink: 0;
  /* Click affordance for "back to Rate Card presentation" (2026-07-06
   * brief). Cursor is spelled out even though anchors default to
   * pointer, so custom themes or third party resets don't override it.
   * Hover feedback is a very subtle opacity shift only — no background,
   * no border, no layout shift, no white box. */
  cursor: pointer;
  transition: opacity 120ms ease;
}
.gnav__logomark:hover { opacity: 0.85; }
.gnav__logomark:focus-visible {
  outline: 2px solid var(--navigation-topbar-text, #FFFFFF);
  outline-offset: 4px;
  border-radius: 4px;
}
.gnav__brand-icon {
  /* Disney "D" vector - Figma node 3606:51561 (2026-07-04 Fidel brief).
   * Native intrinsic size is 32 wide x 28 tall; the wordmark ("Disney
   * Advertising") sits to the right at 18/24. currentColor lets the
   * wireframe theme flip it to grayscale without a second asset. */
  display: block;
  width: 32px;
  height: 28px;
  flex: 0 0 32px;
  color: var(--navigation-topbar-text, #FFFFFF);
  object-fit: contain;
}
.gnav__brand-name {
  /* Per Figma ATLAS Navigation node 3606:51562:
   *   font-family: MultiplaneTWDC Display, weight Bold
   *   font-size:   var(--font/body/normal/lg/font-size, 18px)
   *   line-height: var(--font/body/normal/lg/line-height, 24px)
   *   color:       var(--navigation/topbar/text, #FFFFFF)
   * MultiplaneTWDC is the licensed Disney display face; the fallback
   * stack keeps a humanist grotesque so the lockup reads similarly
   * on machines without the private font. */
  font-family: "MultiplaneTWDC Display", "Manrope", "DM Sans", "Open Sans", Inter, sans-serif;
  font-style: normal;
  font-weight: 700;
  font-size: var(--font-body-normal-xl-font-size, 18px);
  line-height: var(--font-body-normal-xl-line-height, 24px);
  color: var(--navigation-topbar-text, #FFFFFF);
  white-space: nowrap;
  letter-spacing: 0;
}
/* Legacy Disney+Ad-Platforms wordmark SVG: present only for QA-script
 * compatibility. The [hidden] attribute on the element already removes
 * it from layout; this rule belts-and-braces against any selector that
 * forces display:block. */
.gnav__wordmark-svg[hidden] { display: none !important; }
.gnav__wordmark-svg { width: 0; height: 0; }

/* Figma 139-3872 does not render a divider after the brand wordmark; the
 * 386px transparent spacer (139:3876) absorbs the gap to the nav tabs. */
.gnav__divider { display: none; }

/* Product-area tabs (v1.0 only). Anchored in grid column 2 via
 * justify-self:center so the tab group sits at the visual center
 * of the header per the ADS spec. In v1.1 a separate rule sets
 * display:none and the side rail .vnav owns product navigation. */
.gnav__items {
  display: flex;
  align-items: stretch;
  height: 36px;
  margin: 0;
  padding: 0;
  list-style: none;
  gap: var(--navigation-nav-tabs-gap, 4px);
  justify-self: center;
}

/* TopBarTab variant - Figma 795:46..795:53 (ADS top-bar tab).
 *
 * Both inactive and active tabs share the SAME outer height (36px),
 * SAME 8px top padding, and SAME 12px horizontal padding. The
 * difference is what sits below the text inside the 36px box:
 *   - Inactive (795:49 et al): 8px text padding-bottom -> nothing else
 *   - Active   (795:46):        2px padding-bottom + 4px gap + 2px white
 *                               underline (= 8px below text), giving
 *                               the same 36px overall height.
 *
 * The text baseline therefore sits at IDENTICAL y in both states,
 * which is the alignment fix the user requested (the previous
 * implementation used align-items:center which shifted the active
 * text 3px down when padding-bottom changed from 8 to 2).
 *
 * Both states use flex-direction:column + align-items:center +
 * justify-content:flex-start so the text always docks at the top
 * (under the 8px pt) regardless of the underline below. The 36px
 * height is enforced by the padding-y + content height sum, not by
 * a hard height:36px (which would re-introduce centering drift). */
.gnav__item {
  position: relative;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 0;
  padding: 8px 12px;
  box-sizing: border-box;
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: var(--font-body-normal-md-font-size, 14px);
  line-height: var(--font-body-normal-md-line-height, 20px);
  font-style: normal;
  font-weight: 400;
  color: var(--navigation-topbar-text, #FFFFFF);
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  border-radius: 4px;
}
.gnav__item:hover { background: rgba(255, 255, 255, 0.06); }
.gnav__item--active {
  font-weight: 600;
  color: var(--navigation-topbar-text, #FFFFFF);
  /* Per Figma 795:46: pt:8 pb:2 + 4px gap to a 2px underline div.
   * Bottom padding drops from 8 -> 2 so the underline below docks
   * 2px above the tab's bottom edge. The text padding-top stays at
   * 8px so its baseline matches every inactive tab. */
  gap: var(--top-bar-tab-active-gap, 4px);
  padding-bottom: var(--top-bar-tab-active-padding-y, 2px);
}
.gnav__item--active::after {
  /* The white underline sits BELOW the text in the flex column, not
   * as an absolutely-positioned overlay. This guarantees the
   * underline shares the column's intrinsic layout - the only
   * geometric difference between inactive and active is the trailing
   * (4 gap + 2 underline) vs (8 pad-bottom) at the bottom of the
   * 36px box; the TEXT itself never moves. */
  content: "";
  display: block;
  width: 100%;
  height: 2px;
  background: var(--navigation-topbar-text, #FFFFFF);
  flex-shrink: 0;
}

/* Caret rule kept for legacy markup (the new nav has no caret SVG). */
.gnav__caret { display: none; }

/* Right utility cluster - Figma 184:4481 (utilities frame):
 *   w=104, h=32, vertically centered in the 56px nav.
 *   bell (20x20)  at x=0, y=6
 *   apps (20x20)  at x=36, y=6  -> gap=16 between bell and apps
 *   avatar (32x32) at x=72, y=0 -> gap=16 between apps and avatar
 *   Color: navigation/topbar/text = #FFFFFF (all utility icons).
 * (The .gnav__right layout rule itself is defined earlier together with
 *  .gnav__left, in the .gnav 3-col grid block.) */
.gnav__action {
  display: inline-flex;
  width: 32px;
  height: 32px;
  padding: 6px;
  justify-content: center;
  align-items: center;
  box-sizing: border-box;
  background: transparent;
  border: 0;
  border-radius: 999px;
  cursor: pointer;
  color: var(--navigation-topbar-text, #FFFFFF);
  flex-shrink: 0;
}
.gnav__action svg {
  width: 20px;
  height: 20px;
  display: block;
}
.gnav__action:hover { color: var(--navigation-topbar-text, #FFFFFF); background: rgba(255, 255, 255, 0.08); }
.gnav__action:focus-visible {
  outline: 2px solid var(--navigation-topbar-text, #FFFFFF);
  outline-offset: -2px;
}
/* Bell + apps both render in pure white per Figma 184:4483/4485 fill: white. */
.gnav__action--bell,
.gnav__action--apps { color: var(--navigation-topbar-text, #FFFFFF); }

/* --- Top-right utilities icon group, v1.2 only (Figma 375:6250) -----
 * Bug the user reported: the notification, sparkle, and avatar icons
 * are spaced too far apart in v1.2 versus the Figma reference.
 *
 * Root cause: .gnav__action--bell and .gnav__action--apps render as
 * 32x32 buttons with 6px padding around their 20x20 SVGs. .gnav__right
 * uses gap: 16px, so the icon-to-icon distance measures 6+16+6=28px
 * for bell↔apps and 6+16+0=22px for apps↔avatar (the avatar has no
 * padding to match). Figma 375:6250 places the three items in a
 * single flex row with gap:16px directly around the raw icon boxes
 * (20 / 20 / 32), which produces a uniform 16px icon-to-icon gap.
 *
 * Fix: shrink bell + apps to their Figma-spec 20x20 visible size so
 * the existing 16px flex gap on .gnav__right lands as 16px between
 * every icon (20-16-20-16-32 total = 104px, matches Figma). The 20x20
 * SVG artwork, color, tooltip, hover background pill, focus ring, and
 * click handler are all preserved. Accessibility: an invisible
 * ::before extends the pointer target back out to a 32x32 hit area
 * (WCAG 2.5.5 AA target size) without adding any visible spacing
 * between the icons.
 *
 * Scope: v1.2 only. v1.1 keeps the 32x32 button + 16px gap layout so
 * its top-nav renders byte-identically. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .gnav__action--bell,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .gnav__action--apps {
  width: 20px;
  height: 20px;
  padding: 0;
  position: relative;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .gnav__action--bell::before,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .gnav__action--apps::before {
  content: "";
  position: absolute;
  inset: -6px;
}

/* Avatar - Figma 184:4486 reserves a 32x32 circle. Uses the new
 * 1024x1024 Marge headshot supplied with the brief, centered + cover
 * scaled into the 32px circle. No border per the Figma node (clean
 * circular crop of the image). */
.gnav__action--avatar {
  width: 32px;
  height: 32px;
  padding: 0;
  background: none;
  flex: 0 0 auto;
}
.gnav__action--avatar:hover { background: none; }
.gnav__avatar {
  display: inline-block;
  width: 32px;
  height: 32px;
  border-radius: 999px;
  border: 0;
  background-color: #FFFFFF;
  background-image: url("./assets/figma-navigation/avatar.png");
  background-repeat: no-repeat;
  background-position: 50% 35%;        /* bias toward face, not body */
  background-size: cover;
  overflow: hidden;
  flex: 0 0 auto;
}

/* ============================== PAGE LAYOUT ============================== */
/* Figma page (1440 wide) is two stacked bands per node 129-5515:
 *   - White band:  y=  0-174  (nav 0-56, header 85-150)
 *   - Gray band:   y=174 to 1024 (#F7F9FA, Frame 5 holds the table)
 *
 * Header band (Frame 626957): x=68, y=85, w=1312, h=64
 *   -> 68px side gutter at 1440px viewport.
 * Table card (Frame 5): x=2, y=174, w=1438, h=850
 *   -> spans nearly the full viewport width with a 2px outer margin.
 * Inner table content (Frame 627007): x=64, y=23 inside Frame 5,
 *   w=1313, h=625 -> internal padding-x=64, padding-top=23. */
.page {
  width: 100%;
  min-width: 0;
  min-height: calc(100vh - var(--app-header-block-size));
  min-height: calc(100dvh - var(--app-header-block-size));
}

.page__top {
  background: #FFFFFF;
  width: 100%;
  min-width: 0;
  /* No bottom border in Figma. The gray band itself creates the boundary. */
  border-bottom: 0;
}
.page__content {
  background: var(--gray-05);
  width: 100%;
  min-width: 0;
  /* The visible white card sits inside the gray band with a 40px gutter
   * on each side at >=1440 viewport (trimmed from 64 on 2026-06-29 to
   * give the wider 1360px card the room it needs for the new Buying
   * Entity column). Vertical padding keeps the original 24/64 rhythm.
   * Below 1440 viewport the gutter shrinks via the media-query rules
   * further down in this file. */
  padding: 24px 40px 64px;
  box-sizing: border-box;
}

body [data-page="list"] {
  --list-content-inline-end: 24px;
}

body [data-page="list"] > .page__top,
body [data-page="list"] > .page__content {
  padding-right: var(--list-content-inline-end);
}

body [data-page="list"] > .page__content {
  background: var(--page-canvas-background);
  padding-top: calc(var(--navigation-padding-x) + var(--navigation-utilities-gap));
}

@media (max-width: 1023px) {
  body [data-page="list"] {
    --list-content-inline-end: 16px;
  }
}

@media (min-width: 1280px) {
  body [data-page="list"] {
    --list-content-inline-end: 40px;
  }
}

@media (min-width: 1800px) {
  body [data-page="list"] {
    --list-content-inline-end: 60px;
  }
}

/* Compact the list banner without affecting the global navigation or the
 * Create and Edit page headers. Existing navigation spacing values keep the
 * heading stack and centered action row balanced within the white band. */
body [data-page="list"] > .page__top > .page__header {
  padding-top: var(--navigation-padding-x);
  padding-bottom: var(--navigation-padding-x);
}

/* Low elevation is scoped to the list table card only. */
body [data-page="list"] > .page__content > .surface {
  box-shadow: 0 2px 8px rgba(20, 24, 40, 0.10);
}

/* Header content + table card share the SAME center cap and the SAME
 * left/right gutters at >=1440 so the page title, action buttons, and
 * table card all align to the same outer edges. Cap was widened from
 * 1312 -> 1360 on 2026-06-29 to accommodate the new Buying Entity
 * column without forcing horizontal scroll on the table. The gutter
 * was trimmed from 68 -> 40 at 1440 to give the wider card the
 * horizontal room it needs (1360 + 80 = 1440 exact). */
.page__top > .page__header {
  max-width: 1360px;
  margin-left: auto;
  margin-right: auto;
}
.page__top { padding-left: 24px; padding-right: 24px; }
@media (min-width: 1280px) {
  .page__top { padding-left: 40px; padding-right: 40px; }
}
@media (min-width: 1440px) {
  .page__top { padding-left: 40px; padding-right: 40px; }
}

/* Table card. Was 1313 (Figma Frame 627007) - widened to 1360 to fit
 * the new Buying Entity column at 1440 viewport without horizontal
 * scroll. Below 1440 the card shrinks with the viewport (the
 * .page__content padding rule below clamps the outer gutter so the
 * card never touches the viewport edges). */
.page__content > .surface {
  max-width: 1360px;
  margin: 0 auto;
  width: 100%;
}

/* Wide viewport polish (>=1600) - RCM list view only.
 * At 1600+ the previous 1360 cap left 200+ px of empty gutter each
 * side, which made the page feel like a small card floating in an
 * oversized browser. Widen the header + table card in two steps so
 * the page uses available width without stretching so wide that the
 * table columns turn sparse. Both the header (title + action row)
 * and the surface (search + table + pagination) share the same cap
 * so the left/right edges stay aligned. Padding grows in the second
 * breakpoint to keep the visible gutter premium at very wide
 * monitors. Scoped to [data-page="list"] so the Create / Line
 * wizard pages (which have their own fixed-width surface rules per
 * their Figma frames at 1318px) don't misalign their headers vs
 * their inner cards.
 *
 * 2026-07-07: caps bumped (1520 -> 1560, 1680 -> 1760) to reduce the
 * blank space to the right of the table on 1728/1920 monitors. The
 * Name column is now a 1fr flex (see .table__head/.row grid template)
 * so the extra card width is absorbed by the Name column instead of
 * showing as empty gutter beside the last column. */
@media (min-width: 1600px) {
  [data-page="list"] .page__top > .page__header,
  [data-page="list"] .page__content > .surface { max-width: 1560px; }
}
@media (min-width: 1800px) {
  [data-page="list"] .page__top > .page__header,
  [data-page="list"] .page__content > .surface { max-width: 1760px; }
  [data-page="list"] .page__top { padding-left: 60px; padding-right: 60px; }
  [data-page="list"] .page__content { padding-left: 60px; padding-right: 60px; }
}

/* Page header band: Figma 129-5515 Frame 626957 is h=64, starts at y=85.
 * Title at y=0 (h=38), subtitle at y=46 (h=19), buttons at y=12.5 (h=40).
 * Vertical math (from nav bottom y=48 to white→gray boundary y=173):
 *   37 top padding + 65 header + 23 bottom padding + 1 border = 126 → y=174.
 * Figma's border lands at y=173, so bottom padding is 23, not 24. */
.page__header {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  /* Figma 129-5515 Frame 626957 is 64px of visible content (title 36 + 8
   * gap + subtitle 20) and sits at y=85 inside the white band. Nav is
   * 48px tall, so the title should appear 37px below the nav (y=85 = 48
   * + 37). Use height auto + padding-top 37 to land the title at y=85
   * while keeping the inner content stack at 64px. */
  height: auto;
  padding-top: 37px;
  padding-bottom: 23px;
  padding-bottom: 23px;
  box-sizing: content-box;
}
.page__heading { flex: 1; min-width: 0; }
.page__actions { /* see below: moved up so the override applies. */ }
.page__title {
  margin: 0;
  /* ADS font/heading/lg, matching the Accounts Receivable page title.
   * The fallback stack is kept after the token so the brand face still
   * degrades gracefully where it is not installed. */
  color: #000;
  font-family:
    var(--font-heading-lg-font-family, "MultiplaneTWDC Display"),
    "Manrope", "DM Sans", Inter, sans-serif;
  font-size: var(--font-heading-lg-font-size, 24px);
  font-style: normal;
  font-weight: var(--font-heading-lg-font-weight, 500);
  line-height: var(--font-heading-lg-line-height, 28px);
  letter-spacing: var(--font-heading-lg-letter-spacing, 0);
}
.page__subtitle {
  /* Figma 129-5515 subtitle node 129:5530 spec:
   *   body/normal/md: Open Sans Regular 14 / 20
   *   color: #171C20
   * Subtitle sits directly under the title in a flex column with 8px gap. */
  margin: 8px 0 0;
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 20px;
  font-weight: 400;
  color: #171C20;
}
.page__actions {
  display: flex;
  align-items: flex-start;     /* Figma node 65-1570 group: items-start */
  gap: 12px;                   /* Figma: 12px between Import CSV and Create Rate Card */
  margin-top: 16px;            /* Figma button group y=16.5 inside the 65px frame */
  margin-left: auto;
  flex-shrink: 0;
  align-self: flex-start;
}
/* Page-header buttons per Figma 129-5523:
 *   Download blank template : ADS Button tertiary  (gray outline + gray text, 180w)
 *   Upload rate card        : ADS Button secondary (indigo brand outline + text, 160w)
 *   Create rate card        : ADS Button primary   (filled indigo + plus icon, 159w)
 *   All 3 buttons: 36h, radius 6, padding 8/12, gap 4, font Open Sans
 *   SemiBold 14/20, icon 16px on the LEFT in currentColor.
 * Scoped to .page__actions so this override doesn't leak to the delete
 * modal, the filter panel, or the Create Rate Card form actions. */
.page__actions .btn {
  height: 36px;
  padding: 8px 12px;
  border-radius: 6px;
  gap: 4px;
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;
}
.page__actions .btn svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}
/* Page-header secondary (Upload rate card) uses a 1px indigo brand outline,
 * NOT the 2px outline used inside the filter panel. */
.page__actions .btn--secondary {
  border: 1px solid var(--indigo-70);
  background: var(--surface);
  color: var(--indigo-70);
}
.page__actions .btn--secondary:hover { background: var(--indigo-5); }

/* ============================== BUTTONS ============================== */
/* Figma EDL "Label & Inline Button" — both 40px tall.
 *   Primary (Create Rate Card): solid Indigo/70 fill, white text, 6px radius. 165×40 in header.
 *   Secondary (Import CSV): white bg, 1px Indigo/70 border, Indigo/70 text. 131×40 in header.
 * The intrinsic content widths land exactly on those values when we use the
 * EDL paddings (16px sides + 8px gap between icon and text). */
/* Button base - aligned to ADS Button (Figma 17:542, Size=default):
 *   height       36px
 *   padding      12px x / 8px y
 *   radius       6px
 *   font         Open Sans SemiBold 14/20
 *   gap          4px (icon-to-label)
 * .theme-ads overrides below set the brand colors per variant; this base
 * rule supplies the geometry + typography that's identical in both EDL
 * and ADS themes (the ADS Button spec is canonical for the project). */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  height: 36px;
  padding: 8px 12px;
  border: none;
  border-radius: 6px;
  font-family: "Open Sans", "DM Sans", -apple-system, BlinkMacSystemFont,
    "Segoe UI", Helvetica, Arial, sans-serif;
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  letter-spacing: 0;
}
.btn:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: 2px;
}
.btn--primary {
  background: var(--indigo-70);
  color: #FFFFFF;
}
.btn--primary:hover { background: #2C0EA8; }
/* Loading treatment (Create rate card modal Confirm button while the
 * draft is being saved) - a small spinning ring before the "Creating..."
 * label. Reuses the button's existing geometry/disabled state; only the
 * icon + label change. */
.btn.is-loading {
  gap: 8px;
}
.btn.is-loading::before {
  content: "";
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.4);
  border-top-color: #FFFFFF;
  animation: btn-spin 700ms linear infinite;
}
@keyframes btn-spin {
  to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .btn.is-loading::before { animation: none; }
}
.btn--secondary {
  background: var(--surface);
  color: var(--indigo-70);
  /* Figma EDL secondary button uses a 2px Indigo/70 border (measured in export:
   * border occupies 2 contiguous columns at full Indigo/70 fill). */
  border: 2px solid var(--indigo-70);
}
.btn--secondary:hover { background: var(--indigo-5); }
.btn--ghost {
  /* Tertiary text-only button — used for "Save as Draft" on the Create
   * Rate Card page per Figma 70:1660. No background, no border, indigo
   * text, hover gets a subtle tint + underline. Matches EDL link-button
   * pattern. */
  background: transparent;
  color: var(--indigo-70);
  border: 0;
}
.btn--ghost:hover {
  background: var(--indigo-5);
  text-decoration: underline;
}
.btn--ghost:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: 2px;
}
.btn--danger {
  background: var(--danger-btn);
  color: #FFFFFF;
}
.btn--danger:hover { background: var(--danger-btn-hover); }
.btn--sm {
  height: 32px;
  padding: 0 12px;
  border-radius: 6px;
  font-size: 14px;
}

/* ADS Button tertiary variant per ads-components.json (button.variants:
 * primary, secondary, tertiary, ghost, danger) + Figma 129-5515 node
 * 142:4705. Used for Download blank template + Upload rate card on the
 * Rate Card Manager page header, and for the Filter button inside the
 * table toolbar. Renders as a neutral gray-bordered, gray-text button
 * (NOT the indigo brand outline of .btn--secondary).
 *
 * Defined AFTER .btn so the `border: none` reset on .btn doesn't clobber
 * the tertiary border. */
.btn--tertiary {
  background: transparent;
  border: 1px solid rgba(15, 18, 20, 0.5);   /* ADS color/border/bold */
  color: #51585B;                            /* ADS color/text/tertiary */
}
.btn--tertiary:hover {
  background: rgba(15, 18, 20, 0.05);        /* ADS color/surface/hover */
}
.btn--tertiary:focus-visible {
  outline: 2px solid #4045C2;                /* ADS brand focus ring */
  outline-offset: 2px;
}
.btn--tertiary svg { color: currentColor; }
.theme-ads .btn--tertiary {
  border-color: var(--ads-border-bold);
  color: var(--ads-text-tertiary);
}
.theme-ads .btn--tertiary:hover { background: var(--ads-surface-hover); }

.link {
  background: transparent;
  border: none;
  color: var(--indigo-70);
  font-size: 14px;
  font-weight: 500;
  padding: 0;
  cursor: pointer;
}
.link:hover { text-decoration: underline; }

/* Caret used inside buttons */
.caret {
  width: 10px;
  height: 10px;
  display: inline-block;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translateY(-2px);
}

/* ============================== DROPDOWN (Import CSV) ============================== */
.dropdown { position: relative; }
.dropdown__menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 260px;
  background: var(--surface);
  border: 1px solid var(--gray-20);
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(23, 28, 32, 0.06);
  z-index: 30;
  overflow: hidden;
}
.dropdown__item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 10px 14px;
  background: transparent;
  border: none;
  border-top: 1px solid var(--gray-20);
  cursor: pointer;
  font-family: inherit;
}
.dropdown__item:first-child { border-top: none; }
.dropdown__item:hover { background: var(--indigo-5); }
.dropdown__label {
  font-size: 14px;
  font-weight: 500;
  color: var(--gray-95);
}
.dropdown__desc {
  font-size: 14px;
  line-height: 18px;
  color: var(--gray-70);
  margin-top: 2px;
}

/* ============================== TABLE CARD ============================== */
/* Figma 129-5535 / Frame 627007 + the 2026-06-25 elevation brief.
 *
 * The Manager table card uses the SAME Elevation/10 treatment as the
 * Create page's CARD/LINE/PREM sections so the whole prototype reads
 * with one elevation language:
 *
 *   background:     #FFFFFF
 *   border-radius:  16px  (was 8px - bumped to match .create__card)
 *   box-shadow:     0 0 4px 0 rgba(23, 29, 35, 0.13)  (Elevation/10)
 *
 * overflow:hidden keeps row backgrounds, toolbar, and footer from
 * bleeding past the 16px radius. The CARD/LINE/PREM sections need
 * overflow:visible (so popovers and date pickers escape the card
 * bounds) - they override this below in the .create__card rule. */
.surface {
  background: var(--surface);
  border: 0;
  border-radius: 16px;
  box-shadow: 0 0 4px 0 rgba(23, 29, 35, 0.13);
  overflow: hidden;
}
.surface__scroll { width: 100%; }
.surface__inner {
  width: 100%;
  /* No internal x-padding: the gray gutter outside the card already
   * provides the visual inset that matches Figma. The toolbar / table /
   * footer rows each carry their own horizontal padding (16px) per Figma
   * Frame 129-5538 / 129-5817. */
  padding: 0;
  box-sizing: border-box;
}

/* Toolbar - Figma 129-5538 (Data Table / Table Header).
 *   Container: padding 16 top / 16 sides / 8 bottom, h=60.
 *   gap between Filter + Search: 16 (Figma Frame 129:5552 gap-16). */
.toolbar {
  display: flex;
  align-items: center;
  gap: 16px;
  height: 60px;
  padding: 16px 16px 8px;
}

/* Filter button (ADS Button tertiary). */
/* Filter trigger per Figma 129-5515 node 129:5554. ADS Button tertiary
 * 36px tall with funnel icon + "Filter" text. Our DM Sans fallback
 * (DM Sans Medium) renders "Filter" wider than InspireTWDC Medium, so we
 * fix the geometry slightly differently: keep height/border/radius exact,
 * but use min-width and let content size naturally so the 16×16 filter SVG
 * never collapses. svg gets flex-shrink:0 + explicit width/height for safety.
 */
.filter-btn {
  display: inline-flex;
  align-items: center;
  /* Filter button per Figma 129-5515: indigo brand outline + indigo text,
   * 36px tall, 6px radius. Matches the ADS Button secondary variant
   * (brand color) rather than tertiary (gray). */
  gap: 4px;
  height: 36px;
  width: auto;
  min-width: 72px;
  padding: 8px 12px;
  background: var(--surface);
  color: var(--indigo-70);
  border: 1px solid var(--indigo-70);
  border-radius: 6px;
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  font-weight: 600;
  line-height: 20px;
  line-height: 16px;
  cursor: pointer;
  position: relative;
  white-space: nowrap;
  justify-content: center;
}
.filter-btn svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  display: block;
}
.filter-btn:hover { background: var(--indigo-5); }
.filter-btn.is-active { background: var(--indigo-5); }
/* Numeric filter-count badge intentionally removed per design direction.
 * The trigger conveys applied-state via the .is-active background only. */

/* Search input per Figma 129-5515 node 129:5555 (ADS Input search variant):
 * 388x32 pill, white surface by default, brand focus.
 * a 1px Gray/20 hairline appears only on hover, Indigo/70 on focus. */
.search {
  position: relative;
  /* Figma 129-5515 node 129:5555 (ADS Input search): width 388, height 32.
   * Earlier 355px width truncated the placeholder copy. */
  width: 388px;
}
.search__input {
  width: 100%;
  height: 32px;
  padding: 0 36px 0 16px;
  border: none;
  /* ADS Search component spec: rest aliases to surface/default + pill radius
   * for the search variant (Figma renders the search input as a pill). */
  border-radius: 100px;
  background: var(--surface);
  color: var(--gray-95);
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 20px;
  outline: none;
  box-shadow: inset 0 0 0 1px var(--gray-20);
  transition: box-shadow 120ms ease;
}
.search__input:hover { box-shadow: inset 0 0 0 1px var(--gray-30); }
.search__input:focus { box-shadow: inset 0 0 0 1px var(--indigo-70); }
.search__input::placeholder { color: var(--placeholder-color); font-weight: 400; opacity: 1; }
.search__input::-webkit-search-cancel-button { -webkit-appearance: none; }
.search__icon {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--gray-40);
  pointer-events: none;
}

/* ============================== FILTER POPOVER ============================== */
.popover {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  width: 320px;
  background: var(--surface);
  border: 1px solid var(--gray-20);
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(23, 28, 32, 0.08);
  z-index: 20;
  overflow: hidden;
}
.popover__header {
  padding: 14px 16px 10px;
  border-bottom: 1px solid var(--gray-20);
  font-size: 14px;
  font-weight: 500;
  color: var(--gray-95);
}
.popover__body {
  padding: 16px;
  display: grid;
  gap: 14px;
}
.popover__footer {
  border-top: 1px solid var(--gray-20);
  padding: 10px 16px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.popover__footer > .btn { margin-left: auto; }
.field { display: block; }
.field__label {
  display: block;
  font-size: 14px;     /* T10 */
  line-height: 18px;
  font-weight: 500;
  color: var(--gray-70);
  margin-bottom: 4px;
}
.field select {
  width: 100%;
  height: 32px;
  padding: 0 28px 0 10px;
  border: 1px solid var(--gray-30);
  border-radius: 6px;
  background: var(--surface);
  color: var(--gray-95);
  font-size: 14px;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'><path d='M2 4l3 3 3-3' stroke='%23526374' stroke-width='1.4' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 10px center;
  cursor: pointer;
}
.field select:focus { outline: none; border-color: var(--indigo-70); }

/* ============================== TABLE ==============================
 * Exact Figma column widths from node 49:5705 (row x/w values).
 * Total inner row width = 8 (left pad) + 108+400+114+104+72+56+245+128 + 69 (right tail) = 1304.
 * The 'left pad' and 'right tail' are absorbed by the row's 8px container padding.
 *   Status:        x=  8 w=108
 *   Name:          x=116 w=400
 *   Marketplace:   x=516 w=114
 *   Buying Entity: x=630 w=104
 *   Season:        x=734 w= 72
 *   Rows:          x=806 w= 56
 *   Last Updated:  x=862 w=245
 *   Action:        x=1107 w=128 (4 icons × 32)
 *   (residual trailing space: 69px) */
/* Column widths are driven by CSS custom properties so the JS
 * column-resizer (initColumnResize) can update a single :root token
 * per column and both .table__head and every .row track it in sync.
 * The defaults + minimum widths match the 2026-07-08 resize brief.
 *
 * 2026-07-08 overflow brief: the table is now fully non-scrolling.
 * Every non-Name column is a fixed pixel width. The Name track is
 * unconditionally minmax(0, 1fr) - flexible remaining width that
 * shrinks with a 0 minimum so long rate card names truncate with
 * ellipsis instead of forcing horizontal overflow. The old
 * ".name-fixed" branch (which pinned Name to a fixed px value after
 * a manual drag) is retained as an alias for minmax(0, 1fr) so
 * dragging the Name resizer is inert rather than triggering
 * clipped-Action layouts.
 *
 * Fixed pixel widths (default; user-adjustable via column resizers
 * except Name and Action):
 *
 *   Status         96   (brief default; min 88)
 *   SalesHub ID   170   (min 150)
 *   Rate Card ID  200   (min 180)
 *   Name           -    (flexible remaining; ellipsis on overflow)
 *   Marketplace   140   (min 120)
 *   Buying Entity 170   (min 150)
 *   Last updated  140   (min 130)
 *   Ver.           80   (min 72)
 *   Action        170   (min 160; always visible at right edge)
 *
 * Action width bumped +20px on 2026-07-08 to fit the 5th icon
 * (Archive) added between Export and Delete per Figma 129:5535.
 * Icon cluster math: 5 x 24 (icon) + 4 x 8 (gap) + 8 (left pad) =
 * 160px content, 10px slack keeps the rightmost icon (Delete) from
 * kissing the cell edge. Name (minmax(0, 1fr)) absorbs the +20px
 * across all viewports so total row width is unchanged.
 *
 * Fixed-col sum   = 96+170+200+140+170+140+80+170 = 1166
 * Row padding     = 16 L + 16 R = 32
 * Minimum row width to keep Action fully visible = 1198px.
 * At standard desktop breakpoints (1440+) Name absorbs the slack;
 * on the rare very narrow viewport where the card drops below
 * 1178, Name shrinks to 0 and Action still lands within the
 * table-scroll clip region because it's the last column in the
 * grid template. */
.table {
  width: 100%;
  position: relative;      /* anchor for the resize ghost line overlay */
  --col-status:   96px;
  --col-saleshub: 170px;
  --col-rcid:     200px;
  --col-name:     360px;
  --col-market:   140px;
  --col-buyer:    170px;
  --col-updated:  140px;
  --col-ver:       80px;
  --col-action:   170px;
  /* Name track: unconditionally flexible with a 0 minimum so the
   * Name cell can shrink and trigger ellipsis on overflow. The 0
   * minimum is critical - a grid track's default min-content
   * minimum would prevent ellipsis from kicking in and would push
   * fixed columns off-screen. */
  --col-name-track: minmax(0, 1fr);
}
/* Legacy .name-fixed branch (previously toggled by the Name column
 * resizer). Kept as an alias so dragging the Name resizer is inert
 * rather than overflowing the table. */
.table.name-fixed {
  --col-name-track: minmax(0, 1fr);
}
.table__head,
.row {
  display: grid;
  grid-template-columns:
    var(--col-status)
    var(--col-saleshub)
    var(--col-rcid)
    var(--col-name-track)
    var(--col-market)
    var(--col-buyer)
    var(--col-updated)
    var(--col-ver)
    var(--col-action);
  align-items: center;
  column-gap: 0;
  padding-left: 16px;
  padding-right: 16px;
  /* Fill the scroll container exactly. With overflow-x hidden on
   * .table-scroll and Name as minmax(0, 1fr), the row is bounded
   * to the container width and Name absorbs (or gives up) slack
   * to keep the fixed columns visible. */
  width: 100%;
}
.table__head {
  height: 32px;
  background: var(--surface);
  border-bottom: 1px solid var(--gray-20);
}
.th {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0 8px;
  font-family: inherit;
  font-size: 14px;          /* T10 */
  line-height: 18px;
  font-weight: 400;
  color: var(--gray-70);    /* Figma Gray/70 (slightly darker than the earlier guess) */
  white-space: nowrap;
}
/* Sortable header reset. The header cell is a <button> for click + keyboard
 * affordance, but it must look identical to the non-sortable <div> sibling:
 * no chrome, no native button background, inherit font, full-bleed inside
 * the grid column.
 *
 * Layout matches the Figma Header component (129:5557): label + sort glyph
 * sit in a flex row with 4px gap between them. */
.th--sortable {
  background: transparent;
  border: 0;
  margin: 0;
  padding: 0 8px;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  height: 100%;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.th--sortable:hover { color: var(--gray-95); }
.th--sortable:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: -2px;
  border-radius: 2px;
}
/* Ver. header is centered like its body cell. */
.th--sortable.th--ver { text-align: center; justify-content: center; }

/* Selection column. The checkbox column arrives with 2.1, and app.js
 * builds the header cell only there, so earlier versions never have one
 * to lay out. This stays as a guard: if the cell is ever present outside
 * a 2.1 grid it must not claim a track, since the columns after it are
 * positioned by the version's own template. */
.th--select { display: none; }

/* Combined "Rate Card ID / Name" header (2026-08-15, 645:68987). Unlike
 * every other header, this button is a trailing SIBLING in the static
 * index.html markup for every version (see the HTML comment above it),
 * not something app.js only builds under 2.1. Earlier versions' grid
 * templates have no track for it, so left unhidden it falls into
 * whichever track CSS grid auto-placement gives an 11th/extra child,
 * landing on top of Status or Action. Hidden by default here; the 2.1
 * block below is the only place that shows it again. */
.th--name-id { display: none; }

/* Sort glyph - Data Table / Sort Button (Figma 157:1968).
 *
 * The Figma asset is the Feather Arrow-Down&Up icon (node 49:2768):
 * two arrows in a 9x8 viewBox - a DOWN arrow on the left, an UP
 * arrow on the right. Sized to 12x12 in the table header. Color is
 * Figma "EDL Gray/30" (#B8C7D0) for the default/neutral state,
 * which reads as a subtle muted blue-gray that doesn't compete
 * with the column label.
 *
 * The glyph is rendered via an inline <svg> injected by JS so the
 * fill can use currentColor and the active state can just swap the
 * .th__sort element's color (no second SVG, no layout shift). The
 * SVG also responds to color-scheme/dark-mode CSS overrides
 * naturally. The CSS sets size + currentColor; JS owns the markup.
 *
 * Used by every sortable column per Figma: Status, SalesHub ID,
 * Rate Card ID, Name, Marketplace, Last updated, Ver. The Action
 * column is intentionally not sortable (no glyph). */
.th__sort {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 12px;
  height: 12px;
  flex: 0 0 12px;
  /* Default/neutral state: refined ADS neutral gray. The SVG fill is
   * currentColor so this single rule drives both direction paths in the
   * unsorted state. */
  color: #9CA0A8;
  /* Suppress any literal text that might fall back through the
   * element (e.g. CSS counter-content, screen-reader live regions,
   * or a stray content insert). The SVG owns the visual; text would
   * be visual garbage at this size. */
  font-size: 0;
  line-height: 0;
}
.th__sort svg {
  width: 100%;
  height: 100%;
  display: block;
}
/* Each direction is a separate <path> so the active direction can be
 * emphasized without hiding the inactive one. Inactive paths inherit
 * the neutral color from .th__sort. */
.th__sort .th__sort-direction {
  color: inherit;
}

/* Active sort state: label flips to Gray-95 (stronger), only the active
 * direction glyph swaps its fill to the ADS indigo brand. The inactive
 * direction stays at the neutral gray so both arrows remain visible but
 * the sort direction is unmistakable. Layout stays identical (12x12
 * footprint preserved via the same .th__sort box). */
.th.th--sort-asc,
.th.th--sort-desc { color: var(--gray-95); font-weight: 600; }
.th.th--sort-asc .th__sort .th__sort-direction--ascending,
.th.th--sort-desc .th__sort .th__sort-direction--descending {
  color: var(--ads-brand, #4045C2);
}

/* --- Column resize handles (Figma parity, 2026-07-08 brief) --------
 *
 * Each header column (except the last, Action) hosts a .th-resizer
 * span pinned to its right edge. The pattern:
 *
 *   .th (relative)
 *     └── .th-resizer (absolute, 8px wide hit area straddling boundary)
 *           └── ::after (1px visible divider centered inside)
 *
 * States (2026-07-08 rest-hidden brief):
 *   - default: hidden (opacity 0). The header reads as a clean,
 *              quiet row with no vertical bars between columns; the
 *              row underline + text baseline supply enough structure.
 *   - hover:   1px ADS neutral divider (--gray-20). Revealed either
 *              when the user hovers over the header cell OR when the
 *              cursor enters the 8px hit area on the column boundary.
 *              Fades in over 140ms so the reveal feels intentional
 *              rather than jumpy. col-resize cursor over the hit area.
 *   - drag:    1px indigo full-strength; extends top-to-bottom of the
 *              header, and a ghost line spans every body row (see
 *              .table__resize-ghost) so users can eyeball the boundary.
 *
 * The 8px hit area is intentional: WCAG 2.5.5 touch target guidance
 * plus the ADS "adults here, no chunky grab handles" rule from the
 * brief. The visible line stays 1px because that's the ADS table
 * spec; the hit area is invisible and only paints the 1px line on
 * hover / drag.
 *
 * The resizer is a <span> child of the sort <button>. pointerdown +
 * click bubbling from the resizer are stopped in JS so clicking the
 * label sorts, dragging the divider resizes, and clicking the
 * divider itself is a no-op (not a stealthy sort trigger). */
.th { position: relative; }
.th-resizer {
  position: absolute;
  top: 0;
  right: -4px;
  bottom: 0;
  width: 8px;
  cursor: col-resize;
  /* z-index tiers inside .table__head + .row (2026-07-08 sticky-Name
   * pass). Sticky columns MUST fully cover any non-sticky cells that
   * slide under them during horizontal scroll (brief: "columns to the
   * right should scroll underneath/behind the sticky Name column, not
   * overlap visually"). The stack:
   *
   *   0 - non-sticky cells (default)
   *   1 - non-sticky resizers (above their own cell content so the 1px
   *       line is visible against the sort label, but BELOW sticky
   *       columns so their handles disappear when they slide under)
   *   3 - sticky Name + sticky Action (opaque, covers everything
   *       scrolling behind)
   *
   * Sticky columns own their internal resizers via their own stacking
   * context (position:sticky auto-creates one), so Name's own resizer
   * still paints above Name's body content within Name's context. */
  z-index: 1;
  /* Never grab focus - the resizer is pointer-only. Keyboard users
   * still get the full sort/kbd flow because the button owns the
   * tab index. */
  touch-action: none;
}
.th-resizer::after {
  content: "";
  position: absolute;
  top: 6px;
  bottom: 6px;
  left: 3px;                     /* center the 1px line in the 8px hit area */
  width: 1px;
  background: var(--gray-20);    /* ADS neutral divider, revealed on hover */
  opacity: 0;                    /* Hidden at rest per 2026-07-08 brief */
  transition: opacity 140ms ease, background-color 120ms ease;
  pointer-events: none;
}
/* Reveal the 1px handle when the pointer enters either:
 *   a) the header cell (.th:hover)  -> right-edge boundary for that column
 *   b) the 8px resizer hit area (.th-resizer:hover) -> exact boundary
 * Only these interactions light up a handle; the header stays quiet
 * everywhere else. */
.th:hover .th-resizer::after,
.th-resizer:hover::after {
  opacity: 1;
}
.th-resizer.is-dragging::after {
  opacity: 1;
  background: var(--indigo-70);  /* Full-strength ADS primary during drag */
  top: 0;
  bottom: 0;
}

/* Ghost line spanning header + all body rows during an active drag.
 * Added / removed by initColumnResize so it never exists at rest. */
.table__resize-ghost {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--indigo-70);
  pointer-events: none;
  z-index: 3;
  /* Optional soft outline so the line reads on light row backgrounds
   * without over-emphasising it. */
  box-shadow: 0 0 0 1px rgba(54, 17, 200, 0.08);
}

/* Cursor + selection lock while a drag is in progress. Applies to
 * everything inside the table so the col-resize cursor stays sticky
 * even when the pointer briefly leaves the resizer during a fast
 * horizontal drag. */
.table.is-resizing,
.table.is-resizing * {
  cursor: col-resize !important;
  user-select: none;
}

.row {
  height: 48px;
  /* Figma uses Gray/20 (#D4DEE3) for every row divider (measured at
   * (212,222,227) in the export). Don't use a fainter hairline. */
  border-bottom: 1px solid var(--gray-20);
  cursor: default;
}
/* Last row KEEPS its border in Figma. The export shows Gray/20 dividers
 * between every row including after the 10th. Don't suppress it. */

.cell {
  padding: 0 8px;
  font-family: inherit;
  font-size: 14px;             /* T20 */
  line-height: 21px;
  font-weight: 400;
  color: var(--gray-95);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  display: flex;
  align-items: center;
}
/* Action icons + name link keep their own button/link cursors so
 * they don't inherit the row-level pointer cursor with no purpose. */
.row .actions, .row .actions * { cursor: default; }
.row .actions .icon-btn { cursor: pointer; }
.row .name__link { cursor: pointer; }
/* Status cell: the chip needs to align with the column's header "Status"
 * label at +17px from the column left edge. The grid column is left-aligned;
 * cell padding 0 8px + row padding 8px gives us 16px total. Add 1px more
 * to land at exactly +17px to match Figma. */
.cell--status {
  padding-left: 9px;
}

/* Name cell: single Indigo/70 link line, ellipsis on overflow. Figma uses
 * Medium 500 for the link weight (matches the bolder link feel in the Figma). */
.name { padding: 0 8px; min-width: 0; }
.name__link {
  display: inline-block;
  max-width: 100%;
  color: var(--indigo-70);
  font-family: inherit;
  font-size: 14px;
  line-height: 21px;
  /* Figma node 65-1610 spec: link is InspireTWDC Roman (weight 400). The
   * indigo color alone differentiates the link from body text, no need
   * to bump weight. */
  font-weight: 400;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: bottom;
}
.name__link:hover { text-decoration: underline; }
.name__link:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: 2px;
  border-radius: 2px;
}

/* Status chip per ADS Chip component (Figma 137:3432).
 *
 * Resolved variants from Figma 129-5515:
 *   137:3839 (Published) -> ADS Chip style=Info, size=Small
 *     bg #DDE6F7, border #DDE6F7, text #143F90
 *   142:4303 (Draft) -> ADS Chip style=Default, size=Small
 *     bg #ECEEEE, border rgba(15,18,20,0.10), text #1E2528
 *
 * In the Rate Card Manager mockup Figma uses Info for every non-Draft
 * status (Published, Active, Pending Review, Archived) and Default for
 * Draft. This is the canonical ADS Chip mapping for status columns.
 *
 * Chip dimensions (Small variant): pill radius 100, padding 2px / 4px,
 * gap 4, font Open Sans SemiBold 12/18 (body/normal/sm). All variants
 * share these dimensions; only color shifts. */
/* Status chip - matches the project's reference table chip
 * (assets/ref-table-row.png + assets/ref-table.png), the local visual
 * source of truth.
 *
 * Pixel-sampled spec from ref-table-row.png:
 *   Chip box: ~70w x 24h (Published "Published" label), 24h Draft
 *   Border:   1px solid, color per variant
 *   Radius:   pill (100px / 999px - any value >= chip-height/2 reads
 *             identically)
 *   Padding:  0 8px horizontal so the label has visible breathing room
 *             on both sides
 *   Text:     #3611C8 indigo (project --indigo-70) for both variants,
 *             regular weight (NOT bold)
 *   Font:     InspireTWDC (project body font) at a slightly smaller
 *             body size so the label fits cleanly in the 24px chip:
 *             13 / 18 / 400 - measured against ref-table glyph heights
 *
 * Published vs Draft semantic split:
 *   Published / Active / Pending Review / Archived: filled lavender
 *     - background #F3F3FF (very light indigo wash)
 *     - border     #D3D3FF (slightly stronger wash)
 *     - text       #3611C8 (Indigo-70)
 *   Draft: outline ghost
 *     - background #FFFFFF
 *     - border     var(--gray-20)   #D4DEE3
 *     - text       #3611C8 (Indigo-70) - matches reference
 */
.chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: fit-content;
  height: 24px;
  padding: 0 8px;
  border-radius: 999px;
  font-family: "InspireTWDC", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 18px;
  font-weight: 400;
  font-feature-settings: 'liga' off, 'clig' off;
  white-space: nowrap;
  box-sizing: border-box;
  /* Default fallback = filled lavender (covers any callsite that
   * builds a generic .chip without a more specific status modifier). */
  background: #F3F3FF;
  color: var(--indigo-70);
  border: 1px solid #D3D3FF;
}
/* Filled variant - Published (PRD §Status: the Rate Card Manager
 * only ever surfaces .chip--published and .chip--draft; Active /
 * Pending Review / Archived variants were removed 2026-06-25 to
 * align with the two-state model). */
.chip--published {
  background: #F3F3FF;
  color: var(--indigo-70);
  border-color: #D3D3FF;
}
/* Outline ghost variant - Draft */
.chip--draft {
  background: #FFFFFF;
  color: var(--indigo-70);
  border-color: var(--gray-20);
}

/* ===================================================================
 * RATE CARD MANAGER status chips (Figma 157:1980 + 157:2008)
 *
 * Scoped to the Manager table only via .rcm-status-chip. Applied
 * alongside the legacy .chip + .chip--{status} classes so this rule
 * wins through later-in-source specificity without touching the
 * underlying chip primitive (the brief says "do not globally
 * override EDL chip styles").
 *
 * Spec per Figma 157:1980 (Published, Info variant):
 *   bg     #DDE6F7  border #DDE6F7  text #143F90
 * Spec per Figma 157:2008 (Draft, Default variant):
 *   bg     #ECEEEE  border rgba(15,18,20,0.10)  text #1E2528
 * Shared spec:
 *   padding  2px 4px (y x)        radius 100px (pill)
 *   font     Open Sans 12/18 SemiBold (600)
 *   width    fit-content (no stretch); single-line; vertically
 *            centered via inline-flex align-items:center
 *
 * Per the PRD §Status the Rate Card Manager surfaces exactly two
 * visible chip states: Published and Draft. The Active /
 * Pending Review / Archived chip variants that used to live here
 * were removed 2026-06-25; normalizeStatus() in app.js maps any
 * incoming legacy value to Published or Draft before this rule
 * applies, so no other variant class can ever reach the DOM. */
.rcm-status-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: fit-content;
  height: auto;                   /* derived from padding + line-height */
  padding: 2px 4px;
  gap: 4px;
  border: 1px solid transparent;
  border-radius: 100px;
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  line-height: 18px;
  font-weight: 600;
  font-feature-settings: normal;
  white-space: nowrap;
  box-sizing: border-box;
}
/* Published (Figma 157:1980, Info style) */
.rcm-status-chip--published {
  background: #DDE6F7;
  border-color: #DDE6F7;
  color: #143F90;
}
/* Draft (Figma 157:2008, Default style) */
.rcm-status-chip--draft {
  background: #ECEEEE;
  border-color: rgba(15, 18, 20, 0.10);
  color: #1E2528;
}

/* Code columns - SalesHub ID and Rate Card ID render as identifier strings.
 * Use Open Sans (ADS body font) at 13/19 so the long alphanumeric IDs read
 * cleanly without looking like body copy. Truncate with ellipsis on overflow
 * and expose the full value via title attribute (set in JS). */
.cell--code {
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 19px;
  color: var(--gray-95);
  letter-spacing: 0.1px;
}
/* Buying Entity cell. Inherits .cell (nowrap + ellipsis + tooltip on
 * truncation). No extra typography - shares body style with
 * Marketplace and Last updated so the three columns read as a
 * single visual group. The "—" em-dash placeholder picks up the
 * generic .cell color so it stays muted in both ADS and Wireframe
 * themes. Selector exists primarily as an anchor for future
 * column-specific overrides. */
.cell--buying-entity {}
/* Version cell - narrow 48px column, centered numeric. */
.cell--ver {
  justify-content: center;
  padding: 0 4px;
  font-variant-numeric: tabular-nums;
}
.th--ver { justify-content: center; }

/* Action icons - ADS Icon Button: 32x32 hit area, no chrome.
 *
 * Action column is LEFT-aligned (matches every other table column).
 * Icons start from the left edge of the Action cell with 12px gaps
 * between them. This change replaced the prior "right-anchored
 * cluster" design - left-alignment makes the column read as part
 * of the table grid instead of feeling detached on the right edge.
 *
 * Cell padding matches the .cell rule (0 8px) so the first icon
 * lines up with the "Action" header text in the column above.
 *
 * 2026-06-25: tightened per brief.
 *   - Per-icon flex gap reduced from 12px -> 8px so the cluster
 *     reads as one Action group rather than four spread-out icons.
 *   - Icon button visual box reduced from 32x32 -> 24x24 (16px
 *     glyph unchanged + 4px padding instead of 8px). 24x24 still
 *     clears the WCAG 2.5.5 Level AAA minimum target size and is
 *     the brief's recommended compact target.
 *
 * Width math at 5 icons (Quick edit, Duplicate, Export, Archive,
 * Delete - Archive added 2026-07-08 per Figma 129:5535):
 *   5 x 24 (icon) + 4 x 8 (gap) + 8 (left pad) = 160px.
 * The grid-template-columns rule below allocates --col-action 170px
 * (160 + 10 slack) so the rightmost icon (Delete) never kisses the
 * cell edge and the "Action" header still sits comfortably above
 * the cluster. Bumping to 170 shrinks the Name track (minmax(0,
 * 1fr)) by 20px total, which triggers earlier ellipsis on very
 * long rate card names but preserves the overall row width. */
.actions {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 8px;
  padding: 0 0 0 8px;
  width: 100%;
}
.icon-btn {
  /* Compact ADS Icon Button: 24x24 box around a 16x16 glyph
   * (4px padding all sides). The smaller hit target lets the
   * Action cluster sit tighter (per the 2026-06-25 brief) while
   * keeping the click target at the WCAG 2.5.5 AAA minimum. The
   * 16x16 glyph itself is unchanged - it's the .icon-btn box
   * that shrunk, not the visual icon. */
  width: 24px;
  height: 24px;
  padding: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  border-radius: 4px;
  color: var(--indigo-70);
  cursor: pointer;
  box-sizing: border-box;
}
.icon-btn:hover { background: var(--indigo-5); }
.icon-btn:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: -1px;
}
.icon-btn svg { width: 16px; height: 16px; display: block; }

/* Table row action icons (EDL theme fallback) - same no-background-fill
 * treatment as the ADS rule below, so the white-pill artifact never
 * appears regardless of active theme. */
.row .actions .icon-btn,
.row .actions .icon-btn:hover,
.row .actions .icon-btn:focus,
.row .actions .icon-btn:active {
  background: transparent;
}
.row .actions .icon-btn:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: -1px;
  background: transparent;
}

/* ============================== EMPTY ============================== */
.table__empty {
  padding: 64px 24px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 12px;
}
.empty__title {
  font-size: 16px;
  font-weight: 500;
  color: var(--gray-95);
}
.empty__body {
  font-size: 14px;
  color: var(--gray-70);
  max-width: 420px;
  margin-bottom: 8px;
}

/* ============================== FOOTER / PAGINATION ==============================
 * Figma node 49:5940 — 1304 × 48 frame.
 *   Show Results:  152 × 32 @ (16, 8)   ← left-aligned
 *   Default pager: 264 × 24 @ (541, 12) ← centered (mid-x = 673, ≈ 1304/2)
 *   Page Jumper:   110 × 32 @ (1178, 8) ← right-aligned
 * Layout: 3-col grid, outer cols flex so pager naturally centers. */
.footer {
  /* Figma 129-5817 (Pagination footer): 52h, padding 16/8 (sides/y),
   * justify-between, items-center. No bottom border (the table card
   * already has its own outer 1px Gray-20 border). */
  border: 0;
  height: 52px;
  padding: 8px 16px;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 16px;
  background: var(--surface);
}
/* renderTable() hides the footer whenever the filtered result set fits on
 * one page. The attribute selector is needed because the `display` above
 * (and the flex variant in the narrow media query) would otherwise beat the
 * browser's own [hidden] rule and leave a 52px bordered strip behind. */
.footer[hidden] {
  display: none;
}
/* Without the footer the last row's divider would land straight on the
 * card's rounded bottom edge. This is the 8px the footer's own block
 * padding used to put between the rows and whatever came next, so the
 * table still clears the corner without inventing new spacing. */
.table-scroll:has(+ .footer[hidden]) {
  padding-bottom: 8px;
}
.footer__left {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--gray-95);
  font-size: 14px;
  justify-self: start;
}
.footer__right {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--gray-95);
  font-size: 14px;
  justify-self: end;
}
/* ----------------------------------------------------------------------------
 *  Pagination per Figma 129-5515 node 142:4070 (ADS Pagination).
 *
 *  Spec from Figma get_design_context (49:5940):
 *    Frame 1304 × 48, padding 8/16, items spaced 1fr | auto | 1fr.
 *    Left group:   "Show" [select] "of N items"
 *      - Show / of N items: InspireTWDC Roman 12/18, color #171C20
 *      - Select: h 32, padding 8, border 1px #B6C4D3 (gray/30),
 *                radius 8, text 14px #9AABBB (gray/40), chevron 16×16
 *    Center group: «  ‹   1 2 3 … 11   ›  »   gap 16 between sub-groups,
 *                  gap 4 between numbers, each number 24×24 box.
 *      - Chevrons: 16×16 Feather icons. Resting = full color, Disabled = 40%.
 *      - Page number: InspireTWDC 12/18 #171C20, 24×24 cell.
 *      - Active: 2px Indigo/70 underline bar inset 25% from each side
  *                (active item flips to indigo fill + white text).
 *    Right group:  "Go to page" [select]
 *      - Label: 12/18 #171C20
 *      - Select: same chrome as rows-per-page, text 14px #171C20.
 * -------------------------------------------------------------------------- */

/* Footer label text per Figma 129:5818 + 129:5820 spec: InspireTWDC Roman
 * 12/18 #171C20. Labels (Show, of N items, Go to page) wrap the select
 * with 4px gap (Figma node gap-4). */
.footer__left,
.footer__right {
  font-family: "InspireTWDC", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 18px;
  color: var(--gray-95);
  gap: 4px;
}

.footer__left select,
.footer__right select {
  /* ADS Text Box / Select per Figma 129:5818 + 129:5820:
   *   h=32, padding 0/8, border 1px #B6C4D3 (Gray/30), radius 8,
   *   font Open Sans Regular 14/21, chevron 16px on the right. */
  min-width: 64px;
  height: 32px;
  padding: 0 28px 0 8px;
  border: 1px solid #B6C4D3;
  border-radius: 8px;
  background-color: var(--surface);
  font-family: "InspireTWDC", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 21px;
  color: var(--gray-95);
  appearance: none;
  -webkit-appearance: none;
  /* 16×16 chevron-down per Figma (Arrows/Chevron-Down). gray/70 stroke at 1.5 */
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'><path d='M3.5 6L8 10.5L12.5 6' stroke='%23526374' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 8px center;
  cursor: pointer;
}
/* Rows-per-page select's value text is gray/40 in the Figma. Per spec the
 * user can still tell what's selected; we leave it gray/95 so the "10" / "20"
 * value isn't mistaken for disabled. Go-to-page also stays gray/95. */
.footer__left select:focus-visible,
.footer__right select:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: 2px;
}

/* ------ Pager: ADS Pagination per Figma 142:4070 -------------------------
 * Items: 36 x 36, radius 8, white bg, 1px rgba(15,18,20,0.2) border.
 * Active: bg ads/brand (#4045C2), text white, border rgba(15,18,20,0.5).
 * Gap between items: pagination/size/gap = 4px.
 * No first/last chevrons in the ADS pattern, prev/next only.
 * --------------------------------------------------------------------- */
.footer__pager {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  justify-self: center;
}

/* Base hit-target: every page button (number, chevron) is 36x36.
 * Background transparent, no border, no text fill change for the underline
 * pattern. */
.page-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* ADS PaginationItem default per Figma 142:4070:
   *   size 36x36, padding 8, radius 8, border 1px rgba(15,18,20,0.2),
   *   font Open Sans Regular 14. */
  min-width: 36px;
  height: 36px;
  padding: 8px;
  border-radius: 8px;
  background: var(--surface);
  border: 1px solid rgba(15, 18, 20, 0.2);
  color: var(--gray-95);
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  font-weight: 400;
  line-height: 20px;
  font-variant-numeric: tabular-nums;
  margin: 0;
  cursor: pointer;
  position: relative;
}
.page-btn:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: 2px;
}

/* Number cell inherits the 36 x 36 chrome from .page-btn. No override
 * needed for size; just hover + active. */
.page-btn--num:hover:not(.is-active) {
  background: rgba(15, 18, 20, 0.05);
}
.page-btn--num.is-active {
  /* ADS PaginationItem active per Figma 142:4070: bg ads/brand (#4045C2,
   * NOT the EDL indigo #3611C8), text white, border 1px rgba(15,18,20,0.5). */
  background: #4045C2;
  color: #FFFFFF;
  border-color: rgba(15, 18, 20, 0.5);
  font-weight: 400;
}
/* Suppress the prior underline-bar pseudo (legacy active treatment). */
.page-btn--num.is-active::after { content: none; }

/* Pager sub-groups (chevron pair + number row). 4px between siblings. */
.pager__group {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

/* Chevrons inherit the 36 x 36 chrome from .page-btn. Icon 16px inside. */
.page-btn--chevron {
  color: var(--gray-95);
}
.page-btn--chevron svg {
  width: 16px;
  height: 16px;
  display: block;
}
.page-btn--chevron:hover:not(:disabled) {
  background: rgba(15, 18, 20, 0.05);
}
.page-btn--chevron:disabled,
.page-btn--chevron[disabled] {
  cursor: default;
  opacity: 0.40;
}
.page-ellipsis {
  /* ADS PaginationItem variant=ellipsis per Figma 142:4070. Same 36 x 36
   * footprint as the digit/chevron cells but no border, no background;
   * just centered three dots. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  color: var(--gray-95);
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 20px;
  padding: 8px;
}

/* ============================== MODAL ==============================
 * ADS Modal component (Figma DJb3yM8aOQjIAbTQ9Cd8Mi node 38:46).
 * Tokens (resolved through the .theme-ads variable block below):
 *   --modal/size/large/width                640px (NOT used for delete)
 *   --modal/size/default/width              480px (this confirm dialog)
 *   --modal/size/container/radius           12px
 *   --modal/size/padding                    24px (horizontal)
 *   --modal/size/header/padding-y           20px
 *   --modal/size/body/padding-y             16px
 *   --modal/size/footer/padding-y           16px
 *   --modal/size/footer/gap                 12px
 *   --modal/divider                         rgba(15, 18, 20, 0.10)
 *   --modal/background                      #FFFFFF
 *   --modal/title/text                      #1E2528  (Open Sans SemiBold 18/24)
 *   --modal/body/text                       #3B4347  (Open Sans Regular 14/20)
 *   --modal/scrim                           rgba(0, 0, 0, 0.50)
 *
 * Legacy layout (.modal__head + .modal__icon as a colored circle) is
 * deprecated as of 2026-06-29; the .modal--ads variant below is the
 * canonical layout per the Figma reference.
 * ------------------------------------------------------------------- */
.modal {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 50;
  padding:
    max(var(--ads-modal-padding, 24px), var(--app-safe-area-top))
    max(var(--ads-modal-padding, 24px), var(--app-safe-area-right))
    max(var(--ads-modal-padding, 24px), var(--app-safe-area-bottom))
    max(var(--ads-modal-padding, 24px), var(--app-safe-area-left));
}
.modal__scrim {
  position: absolute;
  inset: 0;
  background: var(--scrim);
}

/* -------- Canonical ADS modal layout (variant: .modal--ads) -------- */
.modal--ads .modal__panel {
  position: relative;
  width: var(--ads-modal-width);
  max-width: 100%;
  max-height: calc(
    100vh
    - var(--app-safe-area-top)
    - var(--app-safe-area-bottom)
    - var(--ads-modal-padding)
    - var(--ads-modal-padding)
  );
  max-height: calc(
    100dvh
    - var(--app-safe-area-top)
    - var(--app-safe-area-bottom)
    - var(--ads-modal-padding)
    - var(--ads-modal-padding)
  );
  border: 1px solid var(--ads-modal-divider);
  border-radius: var(--ads-modal-radius);
  background: var(--ads-modal-background);
  box-shadow: none;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.modal--ads .modal__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding:
    var(--ads-modal-header-padding-y)
    var(--ads-modal-padding);
}
/* No leading title icon rule lives here on purpose. ADS Modal 38:46 puts the
 * title flush against the header's 24px inline padding in every variant, so a
 * confirmation says what it will do in words rather than borrowing a warning
 * glyph the system never draws. */
.modal--ads .modal__title {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-family: "Open Sans", -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 18px;
  line-height: 24px;
  font-weight: 600;
  color: var(--ads-modal-title-text);
  letter-spacing: normal;
}
.modal--ads .modal__close {
  flex: 0 0 var(--ads-modal-close-size);
  width: var(--ads-modal-close-size);
  height: var(--ads-modal-close-size);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: var(--ads-modal-close-radius);
  cursor: pointer;
  color: var(--ads-text-tertiary);
  padding: 0;
  transition: color 80ms ease, background 80ms ease;
}
.modal--ads .modal__close:hover {
  color: var(--ads-text-primary);
  background: var(--ads-surface-hover);
}
.modal--ads .modal__close:focus-visible {
  outline: 2px solid var(--ads-brand);
  outline-offset: 2px;
}
.modal--ads .modal__close svg { width: 16px; height: 16px; }
.modal--ads .modal__close-icon {
  display: block;
  width: var(--ads-modal-close-icon-size);
  height: var(--ads-modal-close-icon-size);
  background: currentColor;
  -webkit-mask: url("./assets/ads-modal-close.svg") center / contain no-repeat;
  mask: url("./assets/ads-modal-close.svg") center / contain no-repeat;
}

.modal--ads .modal__divider {
  height: 1px;
  width: 100%;
  background: var(--ads-modal-divider);
  flex: 0 0 1px;
}

.modal--ads .modal__body {
  display: flex;
  flex-direction: column;
  width: 100%;
  gap: 16px;
  padding:
    var(--ads-modal-body-padding-y)
    var(--ads-modal-padding);
  font-family: "Open Sans", -apple-system, BlinkMacSystemFont, sans-serif;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.modal--ads .modal__body-text {
  margin: 0;
  font-size: 14px;
  line-height: 20px;
  font-weight: 400;
  color: var(--ads-modal-body-text);
}

/* Plain read-only summary (NOT an input or field-bordered chrome).
 * Two-line stack: secondary-text label + primary-text value, no
 * background, no border. Per the 2026-06-29 brief: "Show the
 * selected rate card as plain read-only content, not an input
 * field" so users don't mistake the modal for a form. */
.modal--ads .modal__summary {
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* dt + dd both default to block/margin; reset so the stack is
   * driven entirely by the flex gap above. */
}
.modal--ads .modal__summary-label {
  margin: 0;
  font-size: 14px;
  line-height: 20px;
  font-weight: 400;
  color: var(--ads-text-secondary);
  letter-spacing: 0;
}
.modal--ads .modal__summary-value {
  margin: 0;
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;                       /* SemiBold per ADS body emphasis */
  color: var(--ads-text-primary);
  /* Long names wrap onto a second line inside the 480px modal (inner
   * width ~432px after 24px padding) rather than truncating with an
   * ellipsis, so the user can always read the full rate card name
   * they are about to delete. Updated 2026-07-07 per the delete
   * modal content brief: "Do not truncate the name so aggressively
   * that users cannot confirm what they are deleting." The existing
   * data-tooltip on the slot is now a redundancy for keyboard focus,
   * not the primary way to see the full name. */
  white-space: normal;
  overflow-wrap: anywhere;
  letter-spacing: normal;
}

.modal--ads .modal__footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  width: 100%;
  gap: var(--ads-modal-footer-gap);
  padding:
    var(--ads-modal-footer-padding-y)
    var(--ads-modal-padding);
}

/* -------- Create rate card modal (Figma 643:63052 collapsed /
 * 639:61405 expanded) --------------------------------------------------
 * Structure/tokens are the shared .modal--ads shell above; this block
 * only adds: (1) the fade+scale open/close transition scoped to
 * .modal--rc-create so Delete / Quick Edit discard are unaffected, and
 * (2) the "Rate card details (optional)" disclosure section + its
 * two-column Effective Start/End row, which are unique to this modal. */
.modal--rc-create .modal__panel {
  opacity: 0;
  transform: scale(0.96);
  transition: opacity 160ms ease, transform 160ms ease;
}
.modal--rc-create.is-open .modal__panel {
  opacity: 1;
  transform: scale(1);
}
@media (prefers-reduced-motion: reduce) {
  .modal--rc-create .modal__panel { transition: none; }
}

/* Label to control, and control to error, are both 8px in Figma
 * (input/size/text-box/gap), which is exactly the flex gap .theme-ads
 * .field already applies. Two older rules stack on top of that gap and
 * push each control 12px down: the legacy
 * `.field__label { margin-bottom: 4px }` and `.field > .field__error
 * { margin-top: 4px }`, both written when .field was still a block /
 * 4px-gap container. Zeroed inside this modal only; the same 4px sits
 * on ADS fields elsewhere in the app, and unwinding it globally is
 * outside this pass. */
.modal--rc-create .field__label { margin-bottom: 0; }
.modal--rc-create .field > .field__error { margin-top: 0; }

/* Rate card details disclosure header - Figma 643:63069/643:62951:
 * title left (16/22 SemiBold), chevron right (24x24, rotates down/up).
 * Deliberately its own namespace (not .accordion) since this header's
 * layout and chevron semantics differ from the CARD/LINE/PREM accordion
 * cards on the full Edit page. */
.rcm-details__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  padding: 0;
  background: transparent;
  border: 0;
  cursor: pointer;
  text-align: left;
  font-family: "Open Sans", -apple-system, BlinkMacSystemFont, sans-serif;
}
.rcm-details__header:focus-visible {
  outline: 2px solid var(--ads-brand);
  outline-offset: 2px;
  border-radius: 4px;
}
.rcm-details__title {
  font-size: 16px;
  line-height: 22px;
  font-weight: 600;
  color: var(--ads-text-primary);
}
.rcm-details__chev {
  flex: 0 0 24px;
  width: 24px;
  height: 24px;
  color: var(--ads-brand);
  transform: rotate(0deg);         /* collapsed = chevron points down */
  transition: transform 180ms ease;
}
.rcm-details.is-open .rcm-details__chev {
  transform: rotate(180deg);       /* expanded = chevron points up */
}
.rcm-details__help {
  margin: 0;
  font-size: 14px;
  line-height: 20px;
  font-weight: 400;
  color: var(--ads-modal-body-text);
  font-family: "Open Sans", -apple-system, BlinkMacSystemFont, sans-serif;
}
/* Grid-rows reveal: animates from 0fr -> 1fr instead of display:none,
 * so the section expands/collapses smoothly instead of snapping. The
 * body's own overflow:hidden clips content during the transition;
 * once open (1fr = intrinsic height) nothing is actually clipped. The
 * [inert] attribute (toggled in app.js's toggleCreateModalDetails) is
 * what actually removes the collapsed fields from the tab order and
 * blocks pointer interaction - the CSS below only handles the visual
 * reveal.
 *
 * The tracks are minmax(0, Nfr) rather than a bare Nfr because an fr
 * track keeps an `auto` minimum: the collapsed row still reserved the
 * body's 8px padding-top, which left the collapsed modal 8px taller
 * than Figma's 360. An explicit 0 minimum collapses the row to nothing,
 * and the wrap's overflow clips the padding now sitting outside it. */
.rcm-details__wrap {
  display: grid;
  grid-template-rows: minmax(0, 0fr);
  transition: grid-template-rows 220ms ease;
  overflow: hidden;
}
.rcm-details.is-open .rcm-details__wrap {
  grid-template-rows: minmax(0, 1fr);
}
.rcm-details__body {
  display: flex;
  flex-direction: column;
  /* Figma 640:62958: 8px between the header/helper/each standalone
   * field (Buying Entity ID, Buying Entity Display Name, the
   * Marketplace/Season/DCM group, Effective Start/End row). */
  gap: 8px;
  min-height: 0;
  overflow: hidden;
  padding-top: 8px;
}
.rcm-details__body .field { gap: 8px; }
/* Marketplace + Deal Season + DCM Rule Order - Figma 643:62962: this
 * trio is its own visual group with a WIDER 16px internal gap, distinct
 * from the 8px gap .rcm-details__body uses between top-level items. */
.rcm-details__group {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
/* Effective Start / Effective End - Figma 643:62967: two equal columns,
 * 16px gap. Scoped to this modal so it doesn't collide with the full
 * Edit wizard's .create-form__row (32px gap, 3-up). */
.rcm-details__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  width: 100%;
}
@media (max-width: 420px) {
  .rcm-details__row { grid-template-columns: 1fr; }
}

/* Body scroll lock while the Create rate card modal is open - mirrors
 * the Quick Edit sheet's body.qe-open pattern. */
body.rcm-create-open { overflow: hidden; }

/* The same lock for every other ADS Modal (delete, discard, remove line or
 * premium, the shared confirmation). openAdsModal in app.js sets the class,
 * and counts opens, so a dialog raised on top of another does not release the
 * lock when only the top one closes. */
body.ads-modal-open { overflow: hidden; }

/* -------- Legacy layout (kept for any modal still using .modal__head /
 *           .modal__icon / .modal__text / .modal__target as a div).
 *           Not used by the Delete confirmation as of 2026-06-29. ----- */
.modal__panel {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--gray-20);
  border-radius: 8px;
  width: 440px;
  max-width: 92vw;
  overflow: hidden;
}
.modal__head {
  padding: 20px 24px 8px;
  display: flex;
  align-items: flex-start;
  gap: 12px;
}
.modal__icon {
  width: 36px;
  height: 36px;
  flex-shrink: 0;
  border-radius: 9999px;
  background: var(--danger-bg);
  color: var(--danger-text);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.modal__text { flex: 1; min-width: 0; }
.modal__target {
  margin-top: 10px;
  padding: 8px 10px;
  background: var(--gray-05);
  border: 1px solid var(--gray-20);
  border-radius: 6px;
  font-size: 14px;
  color: var(--gray-95);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.modal__footer {
  padding: 16px 24px 20px;
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
/* Generic .modal__title/.modal__body (legacy and ADS share class names
 * for h2/p; .modal--ads overrides above take precedence via specificity) */
.modal__title {
  font-size: 16px;
  font-weight: 500;
  color: var(--gray-95);
  margin-bottom: 4px;
}
.modal__body {
  font-size: 14px;
  color: var(--gray-70);
}

/* ============================== QUICK EDIT BOTTOM SHEET ====================
 * ADS bottom-sheet pattern (ads-components.json id="sheet", side="bottom").
 * Slides up from the bottom of the viewport over a dimmed scrim. Contains:
 *   - sticky header (eyebrow + title + close)
 *   - compact context band (5 readonly meta cells)
 *   - scrollable editable table grid (sticky thead)
 *   - sticky footer (Cancel + Save)
 *
 * Open/close motion: 220ms ease-out on open, 200ms ease-in on close, via
 * an .is-open class that toggles a translateY(100%) -> translateY(0).
 * The [hidden] attribute is removed AFTER the close transition completes
 * (or before the open transition begins) so layout never jumps.
 * ----------------------------------------------------------------------- */
.qsheet {
  position: fixed;
  inset: 0;
  z-index: 1100;            /* above modal scrim (1000), below toast (1200) */
  display: flex;
  align-items: flex-end;
  justify-content: center;
  pointer-events: none;     /* scrim / panel re-enable per-element below */
}
.qsheet[aria-hidden="false"] { pointer-events: auto; }

.qsheet__scrim {
  position: absolute;
  inset: 0;
  background: var(--scrim);
  opacity: 0;
  transition: opacity 200ms ease;
}
.qsheet.is-open .qsheet__scrim { opacity: 1; }

.qsheet__panel {
  position: relative;
  width: 100%;
  height: 76vh;
  height: 76dvh;
  max-height: 880px;
  min-height: min(540px, calc(100dvh - var(--app-safe-area-top)));
  background: #FFFFFF;
  border-top-left-radius: 16px;
  border-top-right-radius: 16px;
  box-shadow: 0 -16px 48px rgba(15, 18, 24, 0.18);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transform: translateY(100%);
  transition: transform 220ms cubic-bezier(0.22, 1, 0.36, 1);
}
.qsheet.is-open .qsheet__panel { transform: translateY(0); }

/* --- Sticky header --- */
.qsheet__head {
  flex: 0 0 auto;
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 20px 28px 16px;
  border-bottom: 1px solid var(--gray-20);
  background: #FFFFFF;
}
.qsheet__head-text { flex: 1; min-width: 0; }
.qsheet__eyebrow {
  font-family: "InspireTWDC", "Open Sans", sans-serif;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--gray-70);
  margin-bottom: 4px;
}
.qsheet__title {
  font-family: "InspireTWDC", "Open Sans", sans-serif;
  font-size: 20px;
  font-weight: 600;
  line-height: 28px;
  color: var(--gray-95);
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.qsheet__close {
  flex: 0 0 auto;
  width: 36px;
  height: 36px;
  border-radius: 8px;
  background: transparent;
  border: 0;
  color: var(--gray-70);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 120ms ease, color 120ms ease;
}
.qsheet__close > svg {
  flex: 0 0 auto;
  pointer-events: none;
}
.qsheet__close:hover { background: var(--gray-10); color: var(--gray-95); }
.qsheet__close:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: 2px;
}

/* --- Shared horizontal gutter ---------------------------------------------
 * The summary band and the editable grid SHARE one 28px horizontal page
 * gutter so column-1 of the table aligns vertically with column-1 of the
 * summary. We apply 20px of gutter on the parent (.qsheet__summary +
 * .qsheet__grid-wrap) and 8px of per-cell padding inside the table - same
 * 8px the main Rate Card table uses (.th padding: 0 8px) - so the cell
 * text edge lands at exactly 28px from the panel edge in both regions.
 *
 *   panel-edge .........28px .... [meta label / cell text starts here]
 *
 * Total = 20 (parent) + 8 (cell padding) = 28.
 * ------------------------------------------------------------------------ */

/* --- Shared column proportions ------------------------------------------
 * ONE proportional column system drives BOTH the top summary band and
 * the editable table below. Column tracks (as % of the shared content
 * width) are declared once as CSS variables on .qsheet__panel and reused
 * downstream by the summary grid and by <col> widths inside the table.
 * The 5 summary cells map to contiguous groups of the 9 table columns
 * (v1.1 shape - four discrete Line condition slots):
 *
 *   Buying Entity   →  adv                (1 col)
 *   Deal Season     →  rate               (1 col)
 *   Marketplace     →  cm + bo            (2 cols)
 *   Record Type     →  ap                 (1 col)
 *   Advertiser      →  lc × 4             (4 cols)
 *
 * Proportions follow the brief's suggested fr weights (1.3 / 0.7 / 0.6 /
 * 0.7 / 1.0 / 0.8×4 = 7.5fr total). Percentages sum to 100% so both the
 * summary grid and the table stretch to fill the panel width evenly at
 * 1440 AND 1920 without leaving giant empty gutters on the right.
 *
 * 2026-07-09 note: v1.2 consolidates the four Line-condition slots into
 * ONE column (Adam's Line-condition update). The override at the bottom
 * of this file re-declares --qe-col-lc to 42.6667% for body[data-version
 * ="1.2"] and JS swaps the <colgroup> + <thead> to the 6-column shape at
 * Quick Edit open time. v1.1 keeps the four 10.6667% tracks below. */
.qsheet__panel {
  --qe-col-adv:  17.3333%;   /* Advertiser + Buying Entity  (1.3fr) */
  --qe-col-rate:  9.3333%;   /* Base Rate + Deal Season     (0.7fr) */
  --qe-col-cm:    8.0000%;   /* Cost Method                 (0.6fr) */
  --qe-col-bo:    9.3333%;   /* Base Offering               (0.7fr) */
  --qe-col-ap:   13.3334%;   /* Ad Product + Record Type    (1.0fr) */
  --qe-col-lc:   10.6667%;   /* Line Condition 1-4          (0.8fr each) */

  /* Derived summary spans (cm+bo, lc×4) so both regions share one truth. */
  --qe-sum-mkt:  17.3333%;   /* Marketplace  = cm + bo     */
  --qe-sum-adv2: 42.6668%;   /* Advertiser   = lc × 4      */
}
/* v1.2 override: merge the four LC tracks into a single 42.6667% track
 * (mathematically equal to --qe-sum-adv2, which is the sum of 4 tracks).
 * JS swaps the <colgroup> to a single <col class="qsheet__col--lc"> at
 * Quick Edit open time in v1.2 mode; that <col>'s width reads from this
 * overridden --qe-col-lc so v1.2 gets one 42.6667% column instead of
 * four 10.6667% columns. v1.1 keeps the base value above. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .qsheet__panel {
  --qe-col-lc: 42.6667%;
}

/* --- Context summary band ---
 * The 5 meta cells map to the table columns below via the shared
 * --qe-col-* variables. Buying Entity sits over Advertiser, Deal Season
 * sits over Base Rate, Marketplace spans Cost Method + Base Offering
 * (so its label sits over Cost Method and its value has breathing room
 * before Record Type), Record Type sits over Ad Product, and Advertiser
 * spans all four Line Condition columns (v1.1) or the single merged
 * Line Conditions column (v1.2 - see the --qe-col-lc override above).
 *
 * No grid gap - the table below has none, so any summary gap would shift
 * Deal Season etc. right of the table column they should sit over. Each
 * .qsheet__meta gets its own 8px left padding to match the table cell's
 * 8px padding, so the LABEL text aligns 28px from the panel edge in both
 * the summary and the table.
 *
 * The Advertiser summary span uses --qe-sum-adv2 (v1.1: sum of 4 LC
 * tracks) which mathematically equals the v1.2-overridden --qe-col-lc
 * (single 42.6667% track), so the label lands over the same visual
 * region in either version. */
.qsheet__summary {
  flex: 0 0 auto;
  display: grid;
  /* Fluid 5-track grid: proportions match the table columns behind
   * each meta cell. Grid stretches to fill the full band width so the
   * whole thing scales with the panel from 1440 to 1920 without any
   * leftover empty gutter on the right. */
  grid-template-columns:
    var(--qe-col-adv)   /* Buying entity */
    var(--qe-col-rate)  /* Deal season */
    var(--qe-sum-mkt)   /* Marketplace  (= cm + bo)  */
    var(--qe-col-ap)    /* Record type */
    var(--qe-sum-adv2); /* Advertiser   (= lc × 4 in v1.1, = merged LC col in v1.2) */
  gap: 0;
  padding: 18px 20px;
  background: var(--gray-05);
  border-bottom: 1px solid var(--gray-20);
}
.qsheet__meta { min-width: 0; padding-left: 8px; }
.qsheet__meta-label {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gray-70);
  margin-bottom: 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.qsheet__meta-value {
  font-size: 14px;
  font-weight: 500;
  color: var(--gray-95);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* --- Body: scrollable grid container --- */
.qsheet__body {
  flex: 1 1 auto;
  min-height: 0;            /* allow inner scroll without overflowing panel */
  overflow: hidden;
}
.qsheet__grid-wrap {
  width: 100%;
  height: 100%;
  overflow: auto;           /* one scroller, vertical + horizontal as needed */
  background: #FFFFFF;
  padding: 0 20px;          /* matches .qsheet__summary horizontal gutter */
  box-sizing: border-box;
}
.qsheet__grid {
  /* Fluid width: the table stretches to fill the wrapper so both
   * regions (summary band above, editable grid here) use the full
   * panel width at 1440 AND 1920 without leaving a giant empty gutter
   * on the right. <col> percentage widths pin the proportions so each
   * column stays aligned with the matching summary cell above.
   *
   * A min-width safety net keeps the columns from collapsing on
   * unusually narrow panels; the wrapper's overflow:auto then adds a
   * horizontal scrollbar for those edge cases. On 1440 and 1920 the
   * min-width is well under available space so no scrollbar appears. */
  width: 100%;
  min-width: 1080px;
  border-collapse: separate;
  border-spacing: 0;
  table-layout: fixed;
}
/* --- Column widths (apply to both <thead> + <tbody>) ----------------------
 * Widths come from --qe-col-* on .qsheet__panel so this table shares
 * ONE proportional grid with the summary band above. The 5 summary
 * cells map to groups of these table columns via CSS variables. See
 * the block that defines --qe-col-* for the full mapping table. */
.qsheet__col--adv  { width: var(--qe-col-adv);  }  /* under Buying Entity  */
.qsheet__col--rate { width: var(--qe-col-rate); }  /* under Deal Season    */
.qsheet__col--cm   { width: var(--qe-col-cm);   }  /* under Marketplace L  */
.qsheet__col--bo   { width: var(--qe-col-bo);   }  /* under Marketplace R  */
.qsheet__col--ap   { width: var(--qe-col-ap);   }  /* under Record Type    */
.qsheet__col--lc   { width: var(--qe-col-lc);   }  /* under Advertiser (v1.1: 4x 10.6667%, v1.2: 1x 42.6667% via override) */

/* --- ADS table-head styling (header row only) -----------------------------
 * Mirrors the main Rate Card table .th rule: inherit font (Open Sans in
 * the ADS theme), 12 / 18 / 400, Gray-70 text, white background, single
 * 1px Gray-20 bottom divider, 8px horizontal padding. Sticky so the
 * header stays visible while the body scrolls. */
.qsheet__th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: #FFFFFF;
  color: var(--gray-70);
  font-family: inherit;
  font-size: 14px;
  line-height: 18px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  text-align: left;
  padding: 8px;
  border-bottom: 1px solid var(--gray-20);
  white-space: nowrap;
  vertical-align: middle;
}
/* Numeric (Base Rate) header is left-aligned so the label sits over the
 * left edge of the input value (which is also left-aligned per brief). */
.qsheet__th--num { text-align: left; }
.qsheet__th--adv { /* width set via colgroup; nothing per-cell needed */ }

.qsheet__row { background: #FFFFFF; }
.qsheet__row + .qsheet__row { border-top: 1px solid var(--gray-15); }
.qsheet__cell {
  padding: 8px;                /* same 8px so cell text aligns under header */
  font-size: 14px;
  color: var(--gray-95);
  border-bottom: 1px solid var(--gray-15);
  vertical-align: middle;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Base Rate cell - left aligned so the input value inside reads left to
 * right matching the rest of the table cells. */
.qsheet__cell--num { text-align: left; }
.qsheet__cell--ro { color: var(--gray-70); }

/* --- Sticky-left Advertiser column, v1.2 only (2026-07-29 brief) ---------
 * Advertiser is the primary identifier column in Quick Edit. Pin it
 * to the left so it stays visible while the mid-columns (Base rate,
 * Cost method, Base offering, Ad product, Line conditions) scroll
 * into view horizontally. Pairs with the existing sticky-top header
 * (.qsheet__th position:sticky, top:0) so the top-left corner cell
 * double-pins (sticky-top + sticky-left) and never scrolls out of
 * view in either axis.
 *
 * z-index tiers:
 *   .qsheet__th (sticky-top only)       = 1
 *   .qsheet__td first-child (sticky-left only)  = 1
 *   .qsheet__th first-child (sticky-top+left corner) = 2
 * so the corner cell always paints above the other sticky cells.
 *
 * The 8px right-edge shadow is shown at rest because Quick Edit's
 * grid is wider than the panel at every viewport the sheet is used
 * (the panel caps around 960px and the grid needs 900+ for its
 * v1.2 6-column layout). No JS toggle needed. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .qsheet__grid thead th:first-child {
  position: sticky;
  left: 0;
  top: 0;
  z-index: 2;
  background: #FFFFFF;
  box-shadow: 8px 0 8px -6px rgba(15, 18, 20, 0.08);
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .qsheet__grid tbody td:first-child {
  position: sticky;
  left: 0;
  z-index: 1;
  background: #FFFFFF;
  box-shadow: 8px 0 8px -6px rgba(15, 18, 20, 0.08);
}
.theme-ads body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .qsheet__grid thead th:first-child,
.theme-ads body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .qsheet__grid tbody td:first-child {
  background: var(--ads-surface, #FFFFFF);
}

/* Compact inline input fits the cell; ADS input look (smaller variant of
 * .field__input). Focus ring uses Indigo-70 to match the rest of the app. */
.qsheet__input {
  width: 100%;
  height: 30px;
  padding: 4px 8px;
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 20px;
  color: var(--ads-input-text);
  background: var(--ads-surface);
  border: var(--ads-input-border-width) solid var(--ads-input-border-rest);
  border-radius: var(--ads-input-radius);
  box-sizing: border-box;
  outline: none;
  transition: border-color 120ms ease, box-shadow 120ms ease, background 120ms ease;
}
.qsheet__input::placeholder { color: var(--placeholder-color); opacity: 1; }
.qsheet__input:hover { border-color: var(--ads-input-border-hover); }
.qsheet__input:focus,
.qsheet__input:focus-visible {
  border-width: var(--ads-input-border-active);
  border-color: var(--ads-input-border-active-color);
  padding: 3px 7px;
  box-shadow: none;
}
/* Base Rate input - left-aligned numeric (per brief). Tabular-nums keeps
 * digits in monospace columns so the decimal points still line up vertically
 * across rows even though the value text starts on the left.
 *
 * max-width keeps the input visually compact (~104px) inside its 136px
 * cell, leaving ~32px of unused space on its right side. Combined with the
 * 8px cell padding on either side of Cost Method, this gives a generous
 * visual gap (~40px) between Base Rate input and Cost Method text. */
.qsheet__input--num {
  text-align: left;
  font-variant-numeric: tabular-nums;
  max-width: 104px;
}
.qsheet__input.is-dirty {
  /* Subtle indigo tint on dirty cells so the operator sees what they touched. */
  background: rgba(54, 17, 200, 0.04);
  border-color: var(--indigo-30, #B8B3F0);
}
.qsheet__input.is-invalid {
  border-color: var(--ads-input-border-error);
  box-shadow: none;
}

/* --- Sticky footer --- */
.qsheet__footer {
  flex: 0 0 auto;
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 16px 28px;
  border-top: 1px solid var(--gray-20);
  background: #FFFFFF;
}
.qsheet__footer .btn[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Lock background scroll while sheet is open. */
body.qe-open { overflow: hidden; }

/* --- ADS theme tweaks --- */
/* --- ADS theme overrides for the Quick Edit bottom sheet -------------------
 * Re-tints every chrome surface, border, divider, text role, input border,
 * focus ring, and the scrim/shadow to ADS tokens (Figma 157-1924 page
 * uses ADS Indigo/300 #4045C2 as the brand color; we map every previous
 * EDL Indigo-70 / Gray-* reference to its ADS-token equivalent). */
.theme-ads .qsheet__scrim {
  background: var(--ads-scrim, rgba(0, 0, 0, 0.50));
}
.theme-ads .qsheet__panel {
  background: var(--ads-surface, #FFFFFF);
  box-shadow: var(--ads-shadow-sheet, 0 4px 16px rgba(15, 18, 20, 0.30));
}
.theme-ads .qsheet__head {
  background: var(--ads-surface, #FFFFFF);
  border-bottom-color: var(--ads-border-recessed);
}
.theme-ads .qsheet__title { color: var(--ads-text-primary); }
.theme-ads .qsheet__eyebrow { color: var(--ads-text-tertiary); }
.theme-ads .qsheet__meta-label { color: var(--ads-text-tertiary); }
.theme-ads .qsheet__meta-value { color: var(--ads-text-primary); }
.theme-ads .qsheet__summary {
  background: var(--gray-05);
  border-bottom-color: var(--ads-border-recessed);
}
.theme-ads .qsheet__th {
  background: var(--ads-surface, #FFFFFF);
  color: var(--ads-table-header-text);
  padding:
    var(--ads-table-header-padding-y)
    var(--ads-table-header-padding-x);
  border-bottom-color: var(--ads-border-default);
  font-family: "Open Sans", sans-serif;
  font-size: var(--ads-table-header-font-size);
  line-height: var(--ads-table-header-line-height);
  font-weight: var(--ads-table-header-font-weight);
}
.theme-ads .qsheet__cell {
  color: var(--ads-text-primary);
  border-bottom-color: var(--ads-border-subtle);
}
.theme-ads .qsheet__cell--ro { color: var(--ads-text-tertiary); }
.theme-ads .qsheet__row + .qsheet__row { border-top-color: var(--ads-border-subtle); }
.theme-ads .qsheet__input {
  background: var(--ads-surface, #FFFFFF);
  color: var(--ads-text-primary);
  border-color: var(--ads-border-default);
}
.theme-ads .qsheet__input::placeholder { color: var(--ads-text-placeholder); }
.theme-ads .qsheet__input:hover { border-color: var(--ads-border-bold); }
.theme-ads .qsheet__input:focus,
.theme-ads .qsheet__input:focus-visible {
  border-color: var(--ads-input-border-active-color);
  box-shadow: none;
}
.theme-ads .qsheet__input.is-invalid,
.theme-ads .qsheet__input.is-invalid:focus,
.theme-ads .qsheet__input.is-invalid:focus-visible {
  border-color: var(--ads-input-border-error);
}
.theme-ads .qsheet__input.is-dirty {
  background: rgba(64, 69, 194, 0.04);
  border-color: var(--ads-brand);
}
.theme-ads .qsheet__footer {
  background: var(--ads-surface, #FFFFFF);
  border-top-color: var(--ads-border-recessed);
}
.theme-ads .qsheet__close { color: var(--ads-text-tertiary); }
.theme-ads .qsheet__close:hover {
  background: var(--ads-surface-hover);
  color: var(--ads-text-primary);
}
.theme-ads .qsheet__close:focus-visible { outline-color: var(--ads-brand); }

/* --- Responsive: collapse the context band on narrow widths --- */
@media (max-width: 900px) {
  .qsheet__panel { height: 84vh; min-height: 0; }
  .qsheet__summary { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; padding: 14px 12px; }
  .qsheet__grid-wrap { padding: 0 12px; }
  .qsheet__meta { padding-left: 8px; }
  .qsheet__head { padding: 16px 20px 14px; }
  .qsheet__footer { padding: 14px 20px; }
  .qsheet__title { font-size: 17px; line-height: 24px; white-space: normal; }
}

/* --- v1.2 Quick Edit summary alignment refinement ------------------------
 * v1.1 keeps its original 5-track layout where the summary is a
 * lossless partition of the 9 table columns:
 *   Buying entity   -> Advertiser
 *   Deal season     -> Base rate
 *   Marketplace     -> Cost method + Base offering  (2-col span)
 *   Record type     -> Ad product
 *   Advertiser      -> Line condition 1..4          (4-col span)
 *
 * v1.2 shifts the last two summary fields one column to the LEFT so
 * their labels sit directly over the table columns they describe:
 *   Buying entity   -> Advertiser                    (unchanged)
 *   Deal season     -> Base rate                     (unchanged)
 *   Marketplace     -> Cost method                   (label position
 *                       unchanged; drops the trailing bo span so bo
 *                       is free for Record type)
 *   Record type     -> Base offering                 (moved LEFT)
 *   Advertiser      -> Ad product                    (moved LEFT)
 *
 * The Line-condition column(s) get NO summary label above them (four
 * columns in v1.1, one merged column in v1.2 per Adam's 2026-07-09
 * update). The summary is a context header for the selection filters
 * (buyer / season / marketplace / record type / advertiser), not a
 * per-column caption. The unclaimed grid tracks stay as gray-05
 * background at the right of the band.
 *
 * Table <col> widths, cell padding, and typography are untouched, so
 * every summary label lands at exactly the same 8px-in indent from the
 * left edge of its corresponding table cell in both regions.
 *
 * The nested media query mirrors the base 900px collapse so v1.2
 * inherits the same 2-column stack on narrow viewports. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .qsheet__summary {
  grid-template-columns:
    var(--qe-col-adv)   /* Buying entity  -> Advertiser col   (unchanged) */
    var(--qe-col-rate)  /* Deal season    -> Base rate col    (unchanged) */
    var(--qe-col-cm)    /* Marketplace    -> Cost method col               */
    var(--qe-col-bo)    /* Record type    -> Base offering col             */
    var(--qe-col-ap);   /* Advertiser     -> Ad product col                */
}
@media (max-width: 900px) {
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .qsheet__summary {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (prefers-reduced-motion: reduce) {
  .qsheet__panel { transition: none; }
  .qsheet__scrim { transition: none; }
}

/* ============================== ADS TOOLTIP ===============================
 *  Implements the ADS Tooltip component (Figma node 73:234) - Type=Small
 *  variant with a single-line label. Mounted once at the bottom of <body>
 *  and positioned per-trigger in JS so it never gets clipped by table card
 *  overflow:hidden / scroll containers.
 *
 *  Figma spec (verified via get_design_context on node 73:234):
 *    Container: bg #21292d, border-radius 6px,
 *               padding-x 16px, padding-y 6px
 *    Text:      Open Sans SemiBold, 12 / 18, white (#FFFFFF)
 *    Arrow:     8px wide x 4px tall (triangle).
 *               For "Top X" arrow positions the arrow sits at the top edge
 *               of the tooltip frame; for "Bottom X" at the bottom edge.
 *               In ADS naming "Bottom Center" arrow = tooltip is ABOVE the
 *               trigger with the caret pointing down at the trigger.
 *    Spacing:   8px gap between trigger and tooltip frame.
 *
 *  Open/close: opacity + 4px vertical slide, 120ms ease-out. The brief
 *  doesn't pin a duration but ADS Tooltip pattern is "quick + subtle".
 * ------------------------------------------------------------------------- */
.ads-tt {
  position: fixed;
  /* Top/left are set in JS; default off-screen for safety. */
  top: 0;
  left: 0;
  z-index: 1300;            /* above modals (1000) + sheet (1100) + toast (1200) */
  pointer-events: none;     /* tooltip never traps clicks or hovers */

  background: #21292D;
  color: #FFFFFF;
  border-radius: 6px;
  padding: 6px 16px;

  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 18px;
  font-weight: 600;
  white-space: nowrap;

  opacity: 0;
  transform: translateY(4px);
  transition: opacity 120ms ease-out, transform 120ms ease-out;
}
.ads-tt[data-ads-tt-visible="true"] {
  opacity: 1;
  transform: translateY(0);
}
.ads-tt__label { display: inline-block; }

/* Arrow: 8px wide x 4px tall triangle pointing toward the trigger.
 * Default placement is "above the trigger" -> arrow draws on the BOTTOM
 * of the tooltip pointing DOWN at the trigger (Figma "Bottom Center").
 * Flip placement (.ads-tt[data-ads-tt-placement="bottom"]) flips the
 * arrow to the TOP of the tooltip pointing UP at the trigger above. */
.ads-tt__arrow {
  position: absolute;
  width: 8px;
  height: 4px;
  left: 50%;
  margin-left: -4px;        /* horizontal-center the 8px arrow */
}
.ads-tt[data-ads-tt-placement="top"] .ads-tt__arrow {
  bottom: -4px;             /* arrow hangs below the tooltip frame */
  border-top: 4px solid #21292D;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
}
.ads-tt[data-ads-tt-placement="bottom"] .ads-tt__arrow {
  top: -4px;                /* arrow sits above the tooltip frame */
  border-bottom: 4px solid #21292D;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
}

@media (prefers-reduced-motion: reduce) {
  .ads-tt { transition: none; transform: none; }
  .ads-tt[data-ads-tt-visible="true"] { transform: none; }
}

/* ============================== ADS TOAST ==========================
 *
 * ADS Toast per Figma file DJb3yM8aOQjIAbTQ9Cd8Mi node 70:62.
 * Layout (row): icon (24px) + main content (title + message) + close X.
 * A 5px left border painted with the variant's accent color anchors
 * the state (success/info/warning/error). Auto-dismiss timing is
 * owned by the JS toast() function (default 5000ms); the close X
 * dismisses manually. Per ADS rule "close buttons are always neutral",
 * the X is Gray/70 regardless of variant.
 *
 * The old anonymous ".toast" pill (dark background, centered) was
 * replaced by this block on 2026-07-07 as part of the Save-and-Create-
 * New brief. All existing `toast(msg)` callers continue to work
 * because the title element collapses when empty (see the
 * .ads-toast__title:empty rule below). */
:root {
  /* Success family - Figma 70:32 uses #6CB288 for accent + #365944 for title. */
  --ads-toast-success-accent: #6CB288;
  --ads-toast-success-text:   #365944;
  /* Info family uses the ADS brand token shown by the blue Toast variant. */
  --ads-toast-info-accent:    var(--ads-brand, #4045C2);
  --ads-toast-info-text:      var(--ads-brand, #4045C2);
  /* Warning family - ADS v2.1 rename yellow -> orange. */
  --ads-toast-warning-accent: #E38900;
  --ads-toast-warning-text:   #7A4A00;
  /* Error family - matches --danger-btn / --danger-text pair. */
  --ads-toast-error-accent:   #D92D20;
  --ads-toast-error-text:     #8A1F16;
  /* Message body text (variant-independent). */
  --ads-toast-body-text:      #1E2528;
  /* Elevation/30 from the Toast component. */
  --ads-toast-radius:         6px;
  --ads-toast-elevation:      0 4px 6px rgba(15, 18, 20, 0.30);
}

/* Toast stack container - fixed to the top-right of the viewport per
 * the 2026-07-07 placement brief:
 *   - top: 88px  -> sits below the Disney Advertising app header (64px
 *                   header + 24px breathing room).
 *   - right: 24px
 *   - z-index: 60 -> above card/table/toolbar/nav so toasts never get
 *                    clipped or covered.
 *
 * flex-direction: column with prepended children (JS uses .prepend())
 * puts newest toast at the TOP of the stack visually. gap: 8px between
 * concurrent toasts matches the brief. pointer-events: none lets clicks
 * in the empty gap between toasts fall through to page content; each
 * .ads-toast re-enables pointer-events for its close button + body. */
.ads-toast-stack {
  position: fixed;
  top: 88px;
  right: 24px;
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
  /* Cap the stack so an overflowing burst of toasts stays contained
   * within the viewport instead of running off the bottom. Individual
   * .ads-toast items pointer-events:auto, so scrolling this container
   * on trackpad still works if a user really produces this many. */
  max-height: calc(100vh - 88px - 24px);
  max-height: calc(100dvh - 88px - 24px);
  overflow-y: auto;
  overflow-x: hidden;
  /* Hide the scrollbar chrome unless truly needed - we're never
   * expecting more than 3-4 stacked toasts in the current flow. */
  scrollbar-width: none;
}
.ads-toast-stack::-webkit-scrollbar { width: 0; height: 0; }

/* Visually hidden but still announced. This existed only under v2 in
   v2.css, and the list page runs on every version, so the filter result
   live region needs it declared here too. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  border: 0;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---- Rate Card List filter drawer -----------------------------------
   The drawer shell itself is the shared .fpanel above. What follows is
   only what the list drawer adds: a scrim, a close button, checkbox
   groups, the buying entity autocomplete, and the applied chips.

   Scoped to .fpanel--list so the Line items drawer, which shares .fpanel
   and deliberately leaves its page undimmed, is untouched. */
.fpanel__scrim {
  position: fixed;
  inset: 0;
  z-index: 39;             /* under .fpanel (40), over the page */
  background: var(--ads-scrim);
  opacity: 0;
  transition: opacity 220ms ease;
}

.fpanel__scrim.is-open {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .fpanel__scrim { transition: none; }
}

.fpanel__header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Neutral, like every other close control in the app. */
.fpanel__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--ads-modal-close-size, 24px);
  height: var(--ads-modal-close-size, 24px);
  padding: 0;
  border: 0;
  border-radius: var(--ads-radius-sm);
  background: transparent;
  color: var(--ads-text-tertiary);
  cursor: pointer;
}

.fpanel__close:hover {
  background: var(--ads-surface-hover);
  color: var(--ads-text-primary);
}

.fpanel__close:focus-visible {
  outline: 2px solid var(--ads-brand);
  outline-offset: 2px;
}

/* A real <fieldset> so the <legend> names the group for a screen
   reader; the browser default border and padding are removed because
   the section hierarchy here comes from type and spacing. */
.fgroup {
  margin: 0;
  padding: 0;
  border: 0;
}

/* Deal season collapses behind a trigger. It is the longest of the three
   lists and every option reads alike, so a summary keeps the drawer
   scannable; the options inside are the same checkboxes as the others. */
.fgroup--menu {
  position: relative;
}

.fgroup__trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  min-height: var(--ads-input-height, 40px);
  padding: 8px 12px;
  border: 1px solid var(--ads-border-default);
  border-radius: var(--ads-radius-sm);
  background: var(--ads-surface);
  color: var(--ads-text-primary);
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 20px;
  text-align: left;
  cursor: pointer;
}

.fgroup__trigger:hover {
  border-color: var(--ads-border-bold);
}

.fgroup__trigger[aria-expanded="true"],
.fgroup__trigger:focus-visible {
  outline: none;
  border-color: var(--ads-brand);
  box-shadow: 0 0 0 1px var(--ads-brand);
}

.fgroup__summary {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.fgroup__caret {
  flex: 0 0 auto;
  color: var(--ads-text-tertiary);
}

/* In flow rather than floating: the drawer body scrolls, and an absolute
   menu would detach from its trigger as soon as it did. */
.fgroup__menu {
  margin-top: 4px;
  padding: 4px;
  max-height: 220px;
  overflow-y: auto;
  border: 1px solid var(--ads-border-default);
  border-radius: var(--ads-radius-sm);
  background: var(--ads-surface);
  box-shadow: var(--ads-shadow-popover);
}

.fgroup__menu[hidden] {
  display: none;
}

.fgroup__legend {
  padding: 0;
  margin-bottom: 8px;
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 18px;
  font-weight: 600;
  color: var(--ads-text-primary);
}

.fgroup__options {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* The whole row is the label, so the click target is the full width of
   the drawer rather than just the 16px box. */
.fgroup__option {
  display: flex;
  align-items: center;
  gap: var(--ads-checkbox-content-gap, 8px);
  padding: var(--ads-checkbox-row-padding, 4px);
  border-radius: var(--ads-radius-sm);
  cursor: pointer;
}

.fgroup__option:hover {
  background: var(--ads-surface-hover);
}

.fgroup__option-text {
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 20px;
  color: var(--ads-text-secondary);
}

/* Buying entity autocomplete. The results list is in flow rather than
   floating: the drawer body scrolls, and an absolutely positioned menu
   would detach from its field as soon as it did. */
.fentity {
  position: relative;
}

.fentity__results {
  list-style: none;
  margin: 4px 0 0;
  padding: 4px;
  max-height: 220px;
  overflow-y: auto;
  border: 1px solid var(--ads-border-default);
  border-radius: var(--ads-radius-sm);
  background: var(--ads-surface);
  box-shadow: var(--ads-shadow-popover);
}

.fentity__result {
  padding: var(--ads-option-pad-y, 8px) var(--ads-option-pad-x, 12px);
  border-radius: var(--ads-radius-sm);
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 20px;
  color: var(--ads-text-secondary);
  cursor: pointer;
}

.fentity__result:hover,
.fentity__result:focus-visible {
  background: var(--ads-surface-hover);
  color: var(--ads-text-primary);
  outline: none;
}

.fentity__result:focus-visible {
  box-shadow: inset 0 0 0 2px var(--ads-brand);
}

.fentity__status {
  margin: 4px 0 0;
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: var(--ads-label-size, 14px);
  line-height: var(--ads-label-line, 18px);
  color: var(--ads-text-tertiary);
}

/* Applied filter chips, between the toolbar and the table. */
.fchips {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 0 16px 12px;
}

.fchips__label {
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: var(--ads-label-size, 14px);
  line-height: var(--ads-label-line, 18px);
  color: var(--ads-text-tertiary);
}

.fchips__list {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.fchips__item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 4px 2px 10px;
  border: 1px solid var(--ads-chip-default-border, var(--ads-border-default));
  border-radius: var(--ads-radius-pill);
  background: var(--ads-chip-default-bg, var(--ads-surface-hover));
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 18px;
  color: var(--ads-chip-default-text, var(--ads-text-primary));
}

.fchips__remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: var(--ads-radius-pill);
  background: transparent;
  color: var(--ads-text-tertiary);
  cursor: pointer;
}

.fchips__remove:hover {
  background: var(--ads-surface-active);
  color: var(--ads-text-primary);
}

.fchips__remove:focus-visible {
  outline: 2px solid var(--ads-brand);
  outline-offset: 1px;
}

.fchips__clear {
  padding: 0;
  border: 0;
  background: transparent;
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 18px;
  color: var(--ads-brand);
  cursor: pointer;
}

.fchips__clear:hover { text-decoration: underline; }

.fchips__clear:focus-visible {
  outline: 2px solid var(--ads-brand);
  outline-offset: 2px;
  border-radius: var(--ads-radius-sm);
}

/* ---- Toast exit -----------------------------------------------------
   Two things happen together when a toast is dismissed. The toast
   itself fades (v2 slides right instead; see v2.css), and the space it
   occupied closes up, so the toasts below it glide into their new
   positions rather than jumping the moment the node detaches.

   --ads-toast-height and --ads-toast-gap are measured in JS just before
   the collapse starts, because neither is knowable from CSS: the height
   depends on how the message wrapped, and the gap belongs to the stack
   and differs per version. The negative bottom margin absorbs the flex
   gap the toast is about to stop needing. */
.ads-toast.is-dismissing {
  animation: ads-toast-fade-out 160ms ease-in both;
  box-sizing: border-box;
  overflow: hidden;
  height: var(--ads-toast-height);
  transition:
    height 200ms cubic-bezier(0.4, 0, 0.2, 1),
    margin-bottom 200ms cubic-bezier(0.4, 0, 0.2, 1),
    padding-top 200ms cubic-bezier(0.4, 0, 0.2, 1),
    padding-bottom 200ms cubic-bezier(0.4, 0, 0.2, 1);
}

.ads-toast.is-dismissing.is-collapsing {
  height: 0;
  padding-top: 0;
  padding-bottom: 0;
  margin-bottom: calc(-1 * var(--ads-toast-gap, 0px));
}

/* Reduced motion removes the toast outright in JS, so there is nothing
   to animate; this only guards against a frame of transition if the
   preference changes mid-dismiss. */
@media (prefers-reduced-motion: reduce) {
  .ads-toast.is-dismissing {
    animation-duration: 1ms;
    transition-duration: 1ms;
  }
}

.ads-toast {
  /* Positioning moved to the .ads-toast-stack parent (2026-07-07).
   * Individual toasts are now static in the flow so they participate
   * in the flex column and stack cleanly. */
  position: static;
  width: 400px;
  max-width: calc(100vw - 48px);
  padding: 16px;
  background: var(--surface, #FFFFFF);
  border: 0;
  /* 5px left border painted per-variant below. Default = info. */
  border-left: 5px solid var(--ads-toast-info-accent);
  border-radius: var(--ads-toast-radius);
  box-shadow: var(--ads-toast-elevation);
  display: flex;
  gap: 12px;
  align-items: flex-start;
  color: var(--ads-toast-body-text);
  /* Individual toasts re-enable pointer-events so the close X + body
   * remain clickable inside the pointer-transparent stack container. */
  pointer-events: auto;
}
.ads-toast[hidden] { display: none; }

/* --- Variant accent (left border + icon color + title color) ------- */
.ads-toast[data-variant="success"] { border-left-color: var(--ads-toast-success-accent); }
.ads-toast[data-variant="info"]    { border-left-color: var(--ads-toast-info-accent); }
.ads-toast[data-variant="warning"] { border-left-color: var(--ads-toast-warning-accent); }
.ads-toast[data-variant="error"]   { border-left-color: var(--ads-toast-error-accent); }

.ads-toast__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  flex: 0 0 24px;
  /* Default = info; overridden below per variant. */
  color: var(--ads-toast-info-accent);
}
.ads-toast__icon img {
  display: block;
  width: 19.5px;
  height: 19.5px;
}
.ads-toast[data-variant="success"] .ads-toast__icon { color: var(--ads-toast-success-accent); }
.ads-toast[data-variant="info"]    .ads-toast__icon { color: var(--ads-toast-info-accent); }
.ads-toast[data-variant="warning"] .ads-toast__icon { color: var(--ads-toast-warning-accent); }
.ads-toast[data-variant="error"]   .ads-toast__icon { color: var(--ads-toast-error-accent); }
/* Legacy toast(msg) callers pass no title; collapse the icon slot
 * when the icon span is empty so a message-only toast doesn't leave
 * an empty left column. */
.ads-toast__icon:empty { display: none; }

.ads-toast__body {
  flex: 1 0 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.ads-toast__title {
  margin: 0;
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 20px;
  font-weight: 700;
  color: var(--ads-toast-info-text);
  word-break: break-word;
}
.ads-toast[data-variant="success"] .ads-toast__title { color: var(--ads-toast-success-text); }
.ads-toast[data-variant="warning"] .ads-toast__title { color: var(--ads-toast-warning-text); }
.ads-toast[data-variant="error"]   .ads-toast__title { color: var(--ads-toast-error-text); }
/* Empty title collapses the row so legacy toast(msg) callers (no
 * title, just a string) still render cleanly as message-only cards. */
.ads-toast__title:empty { display: none; }

.ads-toast__message {
  margin: 0;
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 20px;
  font-weight: 400;
  color: var(--ads-toast-body-text);
  word-break: break-word;
}
.ads-toast__message:empty { display: none; }

.ads-toast__close {
  border: 0;
  background: transparent;
  /* Neutral X per ADS rule: close buttons never tint by variant. */
  color: var(--gray-70);
  cursor: pointer;
  padding: 0;
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
}
.ads-toast__close-icon {
  width: 10px;
  height: 10px;
  background: currentColor;
  -webkit-mask: url("./assets/ads-toast-close.svg") center / 10px 10px no-repeat;
  mask: url("./assets/ads-toast-close.svg") center / 10px 10px no-repeat;
}
.ads-toast__close:hover { color: var(--gray-95); }
.ads-toast__close:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: 2px;
}

/* ============================== FILTER SIDE PANEL ============================
 * 1:1 spec from Figma node 53-10723.
 *   - Aside:   360px wide × full viewport height, anchored top-left (x=0,y=0),
 *              white bg, drop shadow on right edge (Figma: 0 4 4 rgba(0,0,0,.25)).
 *   - Header:  44px tall, white, 1px Gray/20 top border, padding-top 8px.
 *              Title at left (pl-16), Reset Filters link at right (pr-12).
 *   - Body:    flex column, gap 16, padding 16 — fills remaining vertical space.
 *   - Footer:  64px tall, white, 1px Gray/20 top border, drop shadow above,
 *              right-aligned Cancel + Apply with 12px gap.
 *   - No backdrop. Page behind stays visually unchanged (just non-interactive
 *     while the panel is open — handled by JS, not CSS dim).
 *   - Slides in from the left over 220ms when opened. */
/* Filter side panel — Figma node 53-10723.
 * Spec: 360 × full-viewport-height, left-anchored, white bg.
 * Drop shadow: 0 4 4 rgba(0,0,0,0.25) per Figma.
 * Layout: flex column with header (fixed 44h) + body (flex:1) + footer (fixed 64h).
 * Footer must always be anchored at the bottom of the viewport — even if body
 * scrolls. We use top:0 + bottom:0 instead of height:100vh because some browsers
 * report 100vh as less than the actual viewport in headless contexts. */
.fpanel {
  position: fixed;
  /* Below the global nav rather than behind it. The nav is 56px and
     stacks above this panel, so anchoring to the top of the viewport
     put the drawer's own header, its title and its actions, underneath
     the nav where nobody could read or click them. */
  top: var(--app-header-block-size);
  bottom: var(--app-safe-area-bottom);
  left: var(--app-safe-area-left);
  width: 360px;
  background: var(--surface);
  display: flex;
  flex-direction: column;
  z-index: 40;
  box-shadow: 0 4px 4px rgba(0, 0, 0, 0.25);   /* Figma drop shadow */
  transform: translateX(-100%);
  pointer-events: none;
  visibility: hidden;
  transition: transform 220ms ease, visibility 220ms;
}
.fpanel.is-open {
  transform: translateX(0);
  pointer-events: auto;
  visibility: visible;
}

/* Header — 44px tall, title left-aligned, Reset link right-aligned */
.fpanel__header {
  display: flex;
  align-items: center;
  height: 44px;
  padding: 8px 12px 0 16px;
  border-top: 1px solid var(--gray-20);
  gap: 12px;
  flex-shrink: 0;
}
.fpanel__title {
  /* Figma 150:1803: body/bold/xl = Open Sans Bold 18 / 24, color #171C20. */
  margin: 0;
  flex: 1;
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 18px;
  line-height: 24px;
  font-weight: 700;
  color: #171C20;
}
.fpanel__reset {
  background: transparent;
  border: 0;
  padding: 3px 8px;
  border-radius: 8px;
  color: var(--indigo-70);
  font-family: inherit;
  font-size: 14px;
  line-height: 18px;
  font-weight: 400;
  cursor: pointer;
  outline: none;
}
.fpanel__reset:hover { background: var(--indigo-5); }
.fpanel__reset:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: 2px;
}

/* Body — vertical stack of fields with 16px gap, 16px padding all around */
.fpanel__body {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 16px;
  flex: 1 1 auto;
  overflow-y: auto;
}

/* Field — label on top (Gray/60 14/21), select below (32px Gray/30 border). */
.ffield { display: flex; flex-direction: column; gap: 4px; }
.ffield__label {
  font-family: inherit;
  font-size: 14px;
  line-height: 21px;
  font-weight: 400;
  color: #6A8095;            /* EDL Gray/60 */
}
.ffield__input {
  height: 32px;
  width: 100%;
  padding: 0 32px 0 8px;
  background: var(--surface);
  border: 1px solid var(--gray-30);    /* #B6C4D3 */
  border-radius: 8px;
  font-family: inherit;
  font-size: 14px;
  line-height: 21px;
  color: var(--gray-95);
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  /* Figma chevron-down: 16×16 at right with 8px padding */
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'><path d='M3.529 5.529a.667.667 0 0 1 .943 0L8 9.057l3.528-3.528a.667.667 0 0 1 .943.943L8.471 10.47a.667.667 0 0 1-.943 0L3.529 6.472a.667.667 0 0 1 0-.943z' fill='%23526374'/></svg>");
  background-repeat: no-repeat;
  background-position: right 8px center;
}
.ffield__input:hover { border-color: var(--gray-50); }
/* Only show the focus ring for keyboard navigation. Avoids the indigo glow
 * appearing on the select just because the panel auto-focused something on
 * open. */
.ffield__input:focus { outline: none; }
.ffield__input:focus-visible {
  outline: none;
  border-color: var(--indigo-70);
  box-shadow: 0 0 0 1px var(--indigo-70);
}
/* When the "All …" default is selected, render it in muted gray to feel
 * like a placeholder per Figma (Gray/40 #9EB0BD). Once the user picks a
 * real value the text reverts to Gray/95. JS toggles .is-placeholder. */
.ffield__input.is-placeholder { color: var(--placeholder-color); }
.ffield__input option { color: var(--gray-95); }
.ffield__input option[value="all"] { color: var(--gray-40); }

/* ============================================================================
 * ADS Dropdown component (Figma 146:1903 + 150:1791)
 *
 * Used inside the Filter side panel for Status, Marketplace, and Season.
 * Hand-built vanilla DOM stand-in for the ADS Dropdown React component
 * (vanilla repo, no @disney/ads import available). Visual contract per
 * Figma 150-1791:
 *   label    Open Sans Regular 13 / 18  #1E2528  (field-label/state/default/text)
 *   trigger  white bg, 1px rgba(15,18,20,0.2) border, 6px radius
 *            padding 10/12, value text 14/20 #3B4347 (field-description/state/default/text)
 *            20px caret-down icon on the right
 *   menu     white bg, 1px rgba(15,18,20,0.2) border, 6px radius,
 *            4px y padding, drop shadow elevation/30
 *            (0 4 12 rgba(15,18,20,0.3))
 *   option   padding 8/12, Open Sans 14/20 #1E2528,
 *            hover bg rgba(15,18,20,0.05)
 *   selected bg rgba(15,18,20,0.05) (same as hover; Figma shows the
 *            selected option highlighted with the recessed surface)
 * Position: menu sits directly under the trigger inside the panel column
 * (no Popover floating layer needed; the panel already constrains scroll). */
.ads-dd {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
  position: relative;
}
/* The Line items filter drawer builds its fields from .field rather than
   from a hand-written .ads-dd, so its labels are named here instead of
   being restated in v2.css: one declaration keeps the two drawers'
   labels identical by construction. */
.ads-dd__label,
.fpanel--lines .fpanel__body .field__label {
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 18px;
  font-weight: 400;
  color: #1E2528;
}
.ads-dd__trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  height: 40px;
  padding: 10px 12px;
  background: var(--surface);
  border: 1px solid rgba(15, 18, 20, 0.2);
  border-radius: 6px;
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 20px;
  color: #3B4347;
  cursor: pointer;
  text-align: left;
}
.ads-dd__trigger:hover { background: rgba(15, 18, 20, 0.03); }
.ads-dd__trigger:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: 2px;
}
.ads-dd__trigger[aria-expanded="true"] {
  border-color: rgba(15, 18, 20, 0.5);
}
.ads-dd__value {
  flex: 1;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ads-dd__value--placeholder { color: var(--placeholder-color); }
.ads-dd__value--selected    { color: #1E2528; }
.ads-dd__caret {
  flex-shrink: 0;
  color: #1E2528;
  transition: transform 120ms ease;
}
.ads-dd__trigger[aria-expanded="true"] .ads-dd__caret { transform: rotate(180deg); }

/* Menu sits under the trigger inside the panel column. The panel body has
 * overflow-y: auto so the menu can extend past the trigger and the body
 * scrolls naturally. */
.ads-dd__menu {
  list-style: none;
  margin: 4px 0 0 0;
  padding: 4px 0;
  background: var(--surface);
  border: 1px solid rgba(15, 18, 20, 0.2);
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(15, 18, 20, 0.30);
  max-height: 280px;
  overflow-y: auto;
  z-index: 50;
}
.ads-dd__option {
  display: flex;
  align-items: center;
  padding: 8px 12px;
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 20px;
  color: #1E2528;
  cursor: pointer;
  user-select: none;
}
.ads-dd__option:hover { background: rgba(15, 18, 20, 0.05); }
.ads-dd__option.is-selected {
  background: rgba(15, 18, 20, 0.05);
  font-weight: 600;
}
.ads-dd__option:focus-visible {
  outline: none;
  background: rgba(15, 18, 20, 0.10);
}

/* ============================================================================
 * ADS Input + leading/trailing icon (used for the Buying Entity SalesHub ID
 * text search per Figma 150:1813). Visual contract:
 *   label    Open Sans Regular 13 / 18  #1E2528
 *   input    white bg, 1px rgba(15,18,20,0.2) border, 6px radius
 *            padding 10/12 + right padding 36 for the 20px magnifying glass
 *            icon, value text 14/20 #1E2528, placeholder color #3B4347
 *   icon     20x20 magnifying glass, color #1E2528 */
.ads-field {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
}
.ads-field__label {
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: var(--ads-label-size);
  line-height: var(--ads-label-line);
  font-weight: 600;
  color: var(--ads-label-color);
}
.ads-field__input-wrap {
  position: relative;
  width: 100%;
}
.ads-field__input {
  width: 100%;
  height: var(--ads-input-height);
  padding: var(--ads-input-pad-y) 36px var(--ads-input-pad-y) var(--ads-input-pad-x);
  background: var(--ads-surface);
  border: var(--ads-input-border-width) solid var(--ads-input-border-rest);
  border-radius: var(--ads-input-radius);
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 20px;
  color: var(--ads-input-text);
  outline: none;
}
.ads-field__input::placeholder { color: var(--placeholder-color); opacity: 1; }
.ads-field__input:hover { border-color: var(--ads-input-border-hover); }
.ads-field__input:focus-visible {
  border-width: var(--ads-input-border-active);
  border-color: var(--ads-input-border-active-color);
  padding: calc(var(--ads-input-pad-y) - 1px) 35px calc(var(--ads-input-pad-y) - 1px) calc(var(--ads-input-pad-x) - 1px);
  box-shadow: none;
}
.ads-field__input::-webkit-search-cancel-button { -webkit-appearance: none; }
.ads-field__icon {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--ads-text-primary);
  pointer-events: none;
}

/* Footer — 64px, right-aligned, Cancel (outline) + Apply (filled).
 * Drop shadow above per Figma: 0 0 2px rgba(23,29,35,.25) */
.fpanel__footer {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 12px;
  height: 64px;
  padding: 16px;
  border-top: 1px solid var(--gray-20);
  box-shadow: 0 -2px 4px rgba(23, 29, 35, 0.06);
  flex-shrink: 0;
}
/* Figma footer button shape: px-12 py-8 rounded-8 (different from .btn--sm
 * used in the delete modal which is rounded-6). Match it just for the panel.
 * Figma Cancel uses a 2px Indigo/70 border (not 1px). Apply uses a filled
 * Indigo/70 bg with 0 border. Both 32h, 8px radius, T15 (14/16 Medium). */
.fpanel__footer .btn--sm {
  height: 32px;
  padding: 8px 12px;
  border-radius: 8px;
  font-size: 14px;
  line-height: 16px;
  font-weight: 500;
}
.fpanel__footer .btn--secondary {
  border: 2px solid var(--indigo-70);   /* Figma spec: 2px */
  background: var(--surface);
  color: var(--indigo-70);
  /* Compensate for the heavier border so the button still sits at 32h:
   * (2px border × 2 = 4px taken by border, but content padding stays 8/12) */
  padding: 6px 10px;
}
.fpanel__footer .btn--primary {
  background: var(--indigo-70);
  color: #FFFFFF;
  border: 0;
}
.fpanel__footer .btn--secondary:hover { background: var(--indigo-5); }
.fpanel__footer .btn--primary:hover   { background: #2A0DA0; }

/* ============================== UTIL ============================== */
[hidden] { display: none !important; }

/* ============================== DEV: REFERENCE OVERLAY ============================
 * NEVER part of the product. Activated only by ?ref=1 query or the "O" hotkey.
 * Pins the exported Figma frame at 1440×1024 to the top-left of the page so its
 * coordinates align 1:1 with the live HTML at the same viewport width.
 * Default opacity 50%; '[' / ']' nudge by 10%. */
#ref-overlay {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 9000;
}
.ref-overlay__image {
  position: absolute;
  top: 0;
  left: 0;
  width: 1440px;
  height: 1024px;
  opacity: var(--ref-opacity, 0.5);
  pointer-events: none;
  user-select: none;
  /* mix-blend-mode lets matching pixels collapse so only mismatches stand out */
  mix-blend-mode: var(--ref-blend, normal);
}
#ref-overlay.is-flip:hover .ref-overlay__image { opacity: 0; }
.ref-overlay__toolbar {
  position: fixed;
  top: 8px;
  right: 8px;
  z-index: 9001;
  pointer-events: auto;
  background: #111;
  color: #f5f5f5;
  padding: 6px 10px;
  border-radius: 6px;
  font: 12px/1.3 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  box-shadow: 0 4px 12px rgba(0,0,0,.4);
}
.ref-overlay__toolbar strong { letter-spacing: .5px; font-size: 14px; }
.ref-overlay__toolbar label { display: inline-flex; align-items: center; gap: 6px; }
.ref-overlay__toolbar input[type="range"] { width: 80px; }
.ref-overlay__toolbar button {
  background: #333;
  color: #f5f5f5;
  border: 1px solid #555;
  border-radius: 4px;
  padding: 3px 8px;
  cursor: pointer;
  font: inherit;
}
.ref-overlay__hint { color: #888; font-size: 14px; }

/* ============================================================================
 * PAGE ROUTING (list / create / line)
 * ============================================================================
 * Three top-level page regions live in the same DOM. body[data-route] toggles
 * which one is visible. Default = list (the approved Rate Card Manager).
 * Hidden routes get `display:none` so they don't take up layout space and
 * their focusable elements stay out of the tab order. */
.page-route { display: block; }
/* list visible only when route is list (or unset) */
body[data-route="create"] .page-route[data-page="list"] { display: none; }
body[data-route="line"]   .page-route[data-page="list"] { display: none; }
body[data-route="atlas"]  .page-route[data-page="list"] { display: none; }
/* create visible only when route=create */
body:not([data-route="create"]) .page-route[data-page="create"] { display: none; }
/* line visible only when route=line */
body:not([data-route="line"])   .page-route[data-page="line"]   { display: none; }
/* atlas visible only when route=atlas */
body:not([data-route="atlas"])  .page-route[data-page="atlas"]  { display: none; }

/* ============================================================================
 * CREATE RATE CARD PAGE  (Figma node 70:1660)
 * ============================================================================
 * The Create page reuses .page__top / .page__content / .surface from the
 * list page so it inherits all approved responsive + theme behaviors. Only
 * the in-content pieces are new: back link, stepper, form rows.            */

/* --- Top wrap ---------------------------------------------------------------
 *
 * Figma 70:1660 spec for the Create page top band:
 *   back link starts at x=59, title at x=59, stepper centered with x=478,
 *   gray band starts at y=239 (so the white top band is 191px tall).
 *   - back at y=60   → 60-48(nav) = 12px from top of band
 *   - title at y=100 → 100-48 = 52px from top of band
 *   - subtitle at y=146 → bottom of subtitle ≈ 165
 *   - stepper at y=197 → 12px after subtitle (197-165 ≈ 32px from subtitle TOP)
 *   - bottom edge of band y=239 → 239-209(bottom of stepper) = 30px breathing
 *
 * Page gutter = 59px (NOT 68px like the list page — Create uses a narrower
 * gutter so the form card and stepper share the same 59/63 baseline).
 *
 * The .create__header (title + actions) sits inline at y=100, action group
 * right-anchored at x=913. Stepper is its OWN row, centered. */
.create__topwrap {
  /* Edit/Create header content lives inside the same 1318px-wide
   * centered content container as the CARD/LINE/PREM .surface cards
   * below, so back link / title / subtitle on the left and Save as
   * Draft / Save Changes / Save Rate Card on the right share the
   * exact same left/right edges as the form sections.
   *
   * Outer padding lives on .page__top (set below) so at narrow
   * viewports the topwrap and the .surface cards both clip to the
   * same gutter. At >=1436px the 1318px center cap kicks in and
   * matches the surface card width exactly. */
  display: flex;
  flex-direction: column;
  max-width: 1318px;
  width: 100%;
  margin: 0 auto;
  padding: 12px 0 18px 0;              /* 12 top → back link at y=60; 18 bottom → page__top totals 191px */
  gap: 0;                              /* explicit margins on children below */
  box-sizing: border-box;
}

/* --- Back link (← Rate Card Manager) ----------------------------------------
 *
 * Figma 70:1806 spec: plain inline link at x=59 y=60, height 32 (just text).
 * NOT a button/pill. We use an <a> with [data-action] but it must read as a
 * link — no border, no background, no input-box look. */
.create__back {
  display: inline-flex;
  align-self: flex-start;
  align-items: center;
  gap: 4px;
  font-family: "InspireTWDC", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 21px;
  font-weight: 500;
  color: var(--indigo-70);
  text-decoration: none;
  height: 24px;
  margin: 0;
  padding: 0;
  background: transparent;
  border: 0;
  width: auto;
  max-width: max-content;
}
.create__back:hover { text-decoration: underline; }
.create__back:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: 2px;
  border-radius: 2px;
}
.create__back svg { color: currentColor; }

/* --- Page header on the Create page ------------------------------------------
 *
 * Figma 70:1793 spec: header row starts at y=100 (40px gap below back link
 * at y=60). Title at x=59 y=100. Action buttons at x=913 y=110 (vertically
 * centered between title TOP and subtitle BOTTOM = midpoint of the heading
 * block).
 *
 * Layout: flex row, heading takes natural width on the left, actions
 * pushed to the right via margin-left: auto, vertically centered. */
.create__header {
  display: flex;
  flex-wrap: nowrap;
  padding: 0;
  /* Force content-box height auto so the inherited 65px from .page__header
   * doesn't constrain us. We want the box to size to the heading content. */
  box-sizing: border-box;
  height: auto;
  min-height: 0;
  align-items: center;
  margin-top: 16px;
  gap: 24px;
}
[data-page="create"] .page__heading,
[data-page="line"]   .page__heading {
  /* Heading height is ~65px (title 38 + 8 gap + subtitle 21). align-items
   * center on the flex parent will then place actions (32 tall) at vertical
   * midpoint = (65-32)/2 = ~16px from top of heading. */
  flex: 1 1 auto;
  min-width: 0;
}
/* Edit mode hides the "Save and Create New Rate Card" button
 * (only meaningful when creating new rate cards). Draft-only
 * buttons stay visible regardless of mode for now - the brief
 * says "Keep Save as Draft only if the rate card is still Draft"
 * but only Draft rows are editable today, so it's always visible. */
body[data-mode="edit"] [data-create-only] { display: none; }

.create__actions {
  display: flex;
  gap: 12px;
  margin-left: auto;                 /* push to right edge */
  /* Cancel the .page__actions margin-right:-3px nudge that the
   * list-page header uses to align with a Figma export quirk. On
   * the Create / Edit page the actions live inside a max-width
   * container and need to sit flush with the same right edge as
   * the .surface cards below - so reset to 0 here. */
  margin-right: 0;
  flex-wrap: wrap;
  /* Figma 70:1801: actions at x=854 (right-anchored) y=110. With the header
   * starting at y=100, that's a 10px offset from the top. align-self:flex-start
   * + explicit margin-top matches that pixel-precise position. */
  align-self: flex-start;
  margin-top: 10px;
}
/* Create / LINE / PREM step action buttons share the SAME sizing system as
 * the approved list-page header buttons (Import CSV, Create Rate Card):
 *   height           32px
 *   border-radius    8px
 *   padding          8px 12px
 *   font-size        14px
 *   font-weight      500
 *   line-height      16px
 *   gap              8px
 *   border (outline) 2px
 *
 * Width is intentionally allowed to grow with the label (e.g. "Save and
 * Continue to LINE" is wider than Import CSV) — only the *size system*
 * matches, so the buttons feel like they belong to the same family. */
.create__actions .btn {
  /* Per ADS Button spec (Figma 17:542, Size=default): height 36px,
   * padding 12x x 8y, radius 6px, font Open Sans SemiBold 14/20.
   * The .theme-ads .btn rule below already applies the typography; we
   * just need to match the 36px height + 6px radius here so the Create
   * page buttons read at the same proportions as the Manager page
   * buttons (they previously rendered 32h with an 8px radius, which
   * made the text look chunky and overly bold per the brief). */
  height: 36px;
  padding: 8px 12px;
  border-radius: 6px;
  font-size: 14px;
  line-height: 16px;
  gap: 8px;
}
/* Don't add extra horizontal padding to outlined / filled variants — the base
 * 8/12 padding already matches the list-page Create button. Removing the
 * earlier 0 16px override that came from the prior over-sized design. */
.create__actions .btn--primary,
.create__actions .btn--secondary {
  padding: 8px 12px;
}

/* --- Stepper (1 CARD · 2 LINE · 3 PREM) -------------------------------------
 *
 * Figma 70:1807 spec: stepper frame at x=478 y=197, width=610. CENTERED in
 * the 1440 viewport (478 + 610/2 = 783 ≈ 1440/2 = 720, close to centered
 * given small horizontal padding). Inside the frame:
 *   - 504×2 indigo connector bar sits at x=16 y=12 (centered vertically)
 *   - 3 wizard-indicator instances at x=0, x=216, x=422 (gap ≈ 192 each)
 *   - Each indicator: 24×24 dot + 12px gap + label
 *   - Active step: indigo filled dot + white "1"
 *
 * Implementation: a centered flex container with FIXED width matching the
 * Figma. Connectors are explicit elements between steps with a fixed gap-
 * filling width so the stepper sits at the right size and doesn't stretch
 * to the full content area like before. */
.stepper {
  list-style: none;
  /* 32 top → stepper at y=197 (header ends ~165).
   * Per Figma 70:1807 the stepper frame sits at x=478 inside the 1440 page
   * (which puts it ~19px right of true center). We bias the auto-margin to
   * match: auto on both sides centers, but the *content* (first dot) ends
   * up at the center of the auto-margin range. To match Figma's slight
   * right-bias, we override margin-left + margin-right with an explicit
   * pixel offset that keeps the dots in the same place as the Figma frame.
   *
   * Width of rendered stepper content ≈ 505px. Center of stepper content
   * needs to land at x=739.5 (Figma rendered center, measured). With page
   * width 1440 and gutter 59 each side (content area 1322 wide centered),
   * margin-left = 489 - 0 (no offset parent) = 489 from page left, but we
   * need 489 from the .create__topwrap (which has 59px padding-left), so
   * margin-left = 489 - 59 = 430. Subtract half of stepper width (252) for
   * the auto-balance: the centering result lands a bit off-center.
   * Cleanest: use auto on both sides + small left bias. */
  margin: 32px auto 0 auto;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: max-content;
  gap: 0;
}
.stepper__step {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--gray-50);
  font-family: "InspireTWDC", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 21px;
  font-weight: 500;
  position: relative;
  flex: 0 0 auto;
}
.stepper__step + .stepper__step::before {
  /* Per Figma 70:1808 the connector bar is a single 504×2 line behind the
   * 3 dots. With 3 dots (24 each) + 3 labels + 2 connectors + sub-gaps,
   * measured against the actual Figma frame the connector visible length
   * per gap is ~112px. (Was 88 → stepper rendered 47px narrower than
   * Figma's 504px target; +24 per connector closes the gap.) */
  content: "";
  display: inline-block;
  width: 112px;
  height: 2px;
  background: var(--gray-20);
  margin: 0 16px;
  border-radius: 1px;
  flex: 0 0 112px;
}
.stepper__dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 999px;
  background: var(--surface);
  border: 1.5px solid var(--gray-30);
  color: var(--gray-70);
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  flex: 0 0 24px;
}
.stepper__step--active { color: var(--gray-95); }
.stepper__step--active .stepper__dot {
  background: var(--indigo-70);
  border-color: var(--indigo-70);
  color: #FFFFFF;
}

/* --- Stepper step states (used by the LINE step) --------------------------- *
 *
 * On the LINE step the stepper progresses to:
 *   step 1 CARD = COMPLETED  → teal/green ring with checkmark
 *   step 2 LINE = ACTIVE     → indigo filled dot (unchanged from above)
 *   step 3 PREM = INACTIVE   → light gray, locked
 *
 * The connector segment between step 1 and step 2 is also COMPLETED
 * (solid indigo) to signal forward progress. The segment between step 2
 * and step 3 stays the default thin gray. */
.stepper__step--complete { color: var(--gray-50); }
.stepper__step--complete .stepper__dot {
  background: var(--surface);
  /* 1.25px ring so the checkmark dot looks lighter than the indigo-filled
   * active dot — matches Figma's slightly thinner stroke. */
  border: 1.25px solid var(--success-50, #1FA76A);
  color: var(--success-50, #1FA76A);
  padding: 0;
}
.stepper__step--complete .stepper__dot svg { display: block; }
/* Connector AFTER a completed step renders solid indigo so the
 * "progress made" reads visually like the Figma reference. */
.stepper__step--complete + .stepper__step::before {
  background: var(--indigo-70);
  height: 2px;
}

/* Clickable wrapper for completed stepper steps. Visually transparent (the
 * dot/label/chip inside provide the visual chrome) but gives the completed
 * step a real hit-target + keyboard affordance so users can jump back. */
.stepper__btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0;
  margin: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
}
.stepper__btn:hover .stepper__label { color: var(--gray-95); text-decoration: underline; }
.stepper__btn:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Count chip next to each step label (e.g. "1 CARD", "0 LINE", "0 PREM").
 * Per Figma 4-5740: small pill, very light fill, sits to the right of the
 * label with a 6px gap. The DEFAULT chip is the heavily ghosted "0 N"
 * variant used on steps that have no rows yet (LINE, PREM). The completed
 * step (CARD) overrides with a soft indigo wash so the user notices the
 * "1 CARD" count. */
.stepper__chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 18px;
  padding: 0 7px;
  margin-left: 6px;
  font-family: "InspireTWDC", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 18px;
  font-weight: 600;
  /* Ghost default — used on incomplete/zero-count steps. Mirrors Figma's
   * "0 LINE" / "0 PREM" chips: almost-transparent text + 1px Gray/20 border
   * + no fill so the chip reads as a placeholder, not as a value. */
  color: var(--gray-40, #9AABBB);
  background: transparent;
  border: 1px solid var(--gray-20);
  border-radius: 999px;
  letter-spacing: 0.4px;
  white-space: nowrap;
  text-transform: uppercase;
}
/* Completed step's chip uses a soft indigo wash so the user actually
 * notices the count is now non-zero ("1 CARD has been saved"). */
.stepper__step--complete .stepper__chip {
  color: var(--indigo-70);
  background: var(--indigo-5, #F3F3FF);
  border-color: var(--indigo-15, #CFCFFF);
}
/* Active step's chip stays the ghost default since the active step itself
 * is the "0 LINE" — there are no LINE rows saved YET while the user is
 * filling out the LINE form. */

/* --- Form card --------------------------------------------------------------
 * Inherits .surface chrome so it gets the same EDL border + shadow + radius
 * automatically. We just add internal padding for the form content. */
/* Page-content gray band for the Create page:
 *
 * Figma 70:1668: gray band starts at y=239 (191px below the 48px nav).
 * The form card floats inside at y=288 with 49px breathing room at the
 * top of the band. The Create page uses a 59px page gutter (narrower than
 * the list page's 68/40/24 responsive gutters) so the card lands at x=63
 * (4px inset from the 59px gutter so the card is 1314 wide on a 1440
 * viewport, matching Figma).
 *
 * We override only when [data-page="create"] is active so the list page's
 * own gutter rules stay untouched. */
/* The list page's shared .page__top / .page__content rules add a
 * responsive gutter (24/40/68). On the Create page our .create__topwrap
 * already owns the gutter via its own 59px padding — so we zero the
 * shared gutter to avoid stacking. Same on .page__content (we set our
 * own 59px gutter there too). */
[data-page="create"] .page__top,
[data-page="line"] .page__top {
  /* Mirror the gutter on .page__content (59 desktop, 32 / 16
   * responsive below) so .create__topwrap centers inside the same
   * available width as the .surface cards. Without this match the
   * header and the form cards land on different left/right edges
   * at viewports below the 1436px centering breakpoint. */
  padding-left: 59px;
  padding-right: 59px;
}
[data-page="create"] .page__content,
[data-page="line"]   .page__content {
  padding-top: 49px;
  padding-left: 59px;
  padding-right: 59px;
}
[data-page="line"]   .page__content > .surface {
  /* Cancel the global "centered max-width:1304" rule on Line — the
   * card here should fill the gutter-bounded width directly. */
  max-width: none;
  margin: 0;
  width: auto;
}
/* Create page: each section card is locked to the Figma 173:2546 frame
 * width (1318px) and centered. max-width keeps it responsive at narrow
 * viewports. */
[data-page="create"] .page__content > .surface {
  max-width: 100%;
  width: 1318px;
  margin: 0 auto;
}
[data-page="create"] .page__content > .surface + .surface {
  /* keep the 30px gap rule from .create__card; need margin-top because
   * we just reset margin to "0 auto". */
  margin-top: 30px;
}

.create__card,
.theme-ads .create__card,
.theme-ads .surface.create__card {
  /* Figma 173:2547 each CARD/LINE/PREM section frame is 1318px wide
   * and uses the Elevation/10 treatment per the dedicated brief:
   *
   *   background:      #FFFFFF
   *   border-radius:   16px
   *   box-shadow:      0 0 4px 0 rgba(23, 29, 35, 0.13)
   *
   * The third selector (.theme-ads .surface.create__card) is needed
   * because .theme-ads .surface lives later in this file and
   * declares its own border-radius:8 + box-shadow:none for the
   * Manager page table card. Without the same 0,3,0-specificity
   * selector here, the Manager rule would win source-order tie and
   * the CARD/LINE/PREM sections would still render flat.
   *
   * Inner padding stays on .accordion__header / .accordion__body
   * (24/32) so the form-row layout is unchanged. */
  padding: 0;
  margin: 0 auto;
  width: 1318px;
  max-width: 100%;
  background: #FFFFFF;
  border-radius: 16px;
  /* Elevation/10 per Figma 173:2547. Soft, just-lifted feel - never
   * modal/drawer-heavy. */
  box-shadow: 0 0 4px 0 rgba(23, 29, 35, 0.13);
  /* The shared .surface rule sets overflow:hidden so its rounded
   * corners crop content. The CARD/LINE/PREM cards need overflow
   * visible so the elevation shadow renders outside the box AND so
   * portaled dropdown menus / calendar popovers aren't clipped at
   * the section edge. The corner rounding still applies to the card
   * itself; nothing inside the form extends past it visually. */
  overflow: visible;
  /* Belt-and-suspenders: no border on these sections. The shared
   * .surface inherits border:0 already, but a future theme override
   * could re-add one. Locking it here keeps the elevation reading
   * clean. */
  border: 0;
}
[data-page="create"] .page__content > .surface + .surface {
  /* Gap between stacked section cards (CARD, LINE, PREM). */
  margin-top: 30px;
  /* Override .surface's overflow:hidden — the dropdown menus and calendar
   * popovers need to escape the card bounds. */
  overflow: visible;
}
/* Internal form spacing per Figma 70:1670 (CARD details head) + 70:1679..1762
 * (4 form rows). Measured Figma row-to-row pitch is 97px (row height 65 +
 * 32 gap), so .create-form gap is 32px. Section head sits 32px above the
 * first row.
 *
 * Section title: Figma 70:1673 measures 30px tall, which matches a 20-24
 * font-size at line-height 30. Looking at the rendered Figma image the
 * text reads as Bold 20-22px (NOT the heavier 24 I tried). Settle on
 * font 22/30 weight 700 — visually matches Figma's "CARD details" label. */
/* Form row gap defaults to 16 (CARD + LINE per Figma 173:2547 / 173:2597
 * which use content-stretch gap-16). PREM overrides to 32 (Figma 173:2633
 * uses content-stretch gap-32). */
.create-form { display: flex; flex-direction: column; gap: 16px; }
[data-accordion="prem"] .create-form { gap: 32px; }
/* Section head: no extra margin-bottom — the parent flex gap (32) already
 * provides the space to the first row. */
.create-form__sectionhead { margin: 0; }
.create-form__sectiontitle {
  /* Per Figma 70:1673 (30px tall frame) — text reads as 20/30 weight 700.
   * Title sits 15px above helper (45-30=15 from section head measurements). */
  margin: 0 0 15px 0;
  font-family: "InspireTWDC", "DM Sans", Inter, sans-serif;
  font-size: 20px;
  line-height: 30px;
  font-weight: 700;
  color: var(--gray-95);
}
.create-form__sectionhelp {
  margin: 0;
  font-family: "InspireTWDC", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 19px;
  color: var(--gray-70);
}

/* Field rows — Figma row=617+32+617 for 2-up, 400+33+400+32+400 for 3-up.
 * Use CSS grid so all fields in a row stay the same width and align. */
.create-form__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 32px;
}
.create-form__row--three {
  grid-template-columns: 1fr 1fr 1fr;
}
.create-form__row--four {
  grid-template-columns: 1fr 1fr 1fr 1fr;
}

/* CARD details metadata strip - per Figma 173:2546 (frame 181:4064)
 * the Rate Card ID is shown as a single bold inline line at the top
 * of the CARD section, not as a disabled input. Label sits at 12/18
 * Open Sans Bold #3B4347 (field-description default text) and the
 * value sits in the muted ADS disabled-text color (rgba(15,18,20,0.2))
 * until a draft ID is minted. After save the same span fills with the
 * actual ID at the same weight. */
.create-form__metastrip {
  margin: 0;
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  line-height: 18px;
  font-weight: 700;
  color: var(--ads-label-color, #3B4347);
}
.create-form__metastrip-label {
  color: var(--ads-label-color, #3B4347);
  margin-right: 4px;
}
.create-form__metastrip-value {
  font-weight: 700;
  color: var(--ads-label-color, #3B4347);
}
/* When showing the default "Auto-generated on Save" placeholder copy
 * (no draft yet) use the muted Figma 181:4062 tone. JS toggles
 * .is-default by comparing textContent to data-default. */
.create-form__metastrip-value.is-default {
  color: rgba(15, 18, 20, 0.2);
}

/* Field helper text - subtle one-liner below an input. Used on the PREM
 * effective start/end fields to explain inheritance behavior. */
.field__helper {
  margin: 6px 0 0 0;
  font-family: "InspireTWDC", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 20px;
  color: var(--gray-70);
}
.theme-ads .field__helper { color: var(--ads-text-tertiary); }

/* --- Accordion (Create page section cards) --------------------------------
 * Each .create__card on the Create page wraps an accordion: a clickable
 * header (chevron + title) followed by a collapsible body. Default state:
 * is-open (all 3 sections expanded). Toggle wire-up lives in app.js. */
/* Accordion header - matches Figma 173:2550 (chevron) + 173:2552 (title).
 *
 * Layout per Figma parent frame (Frame 627170, 177w x 24h):
 *   chevron 24x24 at x=0
 *   title at x=28 (chevron 24 + 4px gap = 28)
 *   header total height = title line-height (28px)
 *
 * Typography per Figma 173:2552 (font/heading/lg):
 *   family   MultiplaneTWDC Display Medium (fallback to Open Sans
 *            for the public web - the brand face is private)
 *   size     24px
 *   line-h   28px
 *   color    #161C1E (gray/800)
 *
 * The chevron + title are vertically centered on the same row via
 * align-items:center and the chevron is immediately to the left of the
 * title (small 4px gap). This is the tighter, locked-up Figma layout. */
.accordion__header {
  display: flex;
  align-items: center;
  gap: 4px;                       /* Figma chevron-to-title spacing */
  width: 100%;
  padding: 24px 24px 0 24px;      /* card top padding (Figma 24/32 card spec) */
  background: transparent;
  border: 0;
  cursor: pointer;
  font-family: inherit;
  color: #161C1E;                 /* Figma gray/800 */
  text-align: left;
}
.accordion__header:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: -2px;
  border-radius: 4px;
}
.theme-ads .accordion__header { color: var(--ads-text-primary); }
.theme-ads .accordion__header:focus-visible { outline-color: var(--ads-brand); }

/* Chevron - 24x24, MultiplaneTWDC indigo, rotates 90deg when collapsed.
 * Drawn as chevron-down (pointing down at rest) so the OPEN state needs
 * no rotation and the CLOSED state rotates -90 deg to point right. */
.accordion__chev {
  flex: 0 0 24px;
  width: 24px;
  height: 24px;
  color: var(--indigo-70);
  transition: transform 180ms ease;
  transform: rotate(-90deg);      /* default = closed = chev points right */
}
.theme-ads .accordion__chev { color: var(--ads-brand); }
.accordion.is-open .accordion__chev {
  transform: rotate(0deg);        /* open = chev points down */
}

/* Title - Figma heading/lg: MultiplaneTWDC Display Medium 24/28.
 * MultiplaneTWDC is the canonical Disney heading face; fall back through
 * Open Sans (already loaded for ADS body) for public-web reliability. */
.accordion__title {
  font-family: "MultiplaneTWDC Display", "Open Sans", -apple-system,
    BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  font-size: 24px;
  font-weight: 500;               /* Medium */
  line-height: 28px;
  letter-spacing: 0;
  color: inherit;
}
.theme-ads .accordion__title {
  font-family: "MultiplaneTWDC Display", "Open Sans", -apple-system,
    BlinkMacSystemFont, sans-serif;
}
.accordion__body {
  /* The body keeps form values in the DOM when collapsed - we hide via
   * display:none but never re-render. Inputs and their state persist. */
  padding: 16px 24px 32px 24px;
  display: block;
}
.accordion:not(.is-open) .accordion__body { display: none; }
.accordion:not(.is-open) .accordion__header { padding-bottom: 24px; }

/* ----------------------------------------------------------------------
 * Accordion header status slot (2026-06-29 progressive-disclosure brief).
 * Right-anchored chip next to the section title:
 *   - error variant: "N required fields missing" in ADS danger text.
 *   - completed variant: subtle check + "Completed" caption in
 *     ADS success text. Both states are deliberately quiet so the
 *     section header doesn't read as a chip cluster.
 * The slot is empty by default and only renders after the first
 * Save attempt (validateForm populates it via renderAccordionStatuses).
 * ------------------------------------------------------------------- */
.accordion__status {
  margin-left: auto;                  /* pushes to the right edge of header */
  font-family: "Open Sans", -apple-system, sans-serif;
  font-size: 14px;
  font-weight: 500;
  line-height: 20px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  letter-spacing: -0.005em;
  color: var(--gray-70);              /* neutral fallback before validate */
}
.accordion__status:empty { display: none; }

.accordion__status--error {
  color: var(--ads-danger-text, #993B44);
}
.accordion__status--ok {
  color: var(--ads-positive-text, #2E7D32);
}
.accordion__status--ok svg { color: var(--ads-positive-text, #2E7D32); }
/* The completed caption stays the same Open Sans weight as the error
 * variant so the two states feel like one family. */
.accordion__status--ok span { font-weight: 500; }

/* Errored sections get a hairline red left-edge accent so collapsed
 * cards can still signal trouble without painting the whole surface
 * red (brief: "do not make the whole section overly red"). 3px ADS
 * danger border, left edge only, scoped to the rounded card surface. */
.accordion.accordion--has-errors {
  box-shadow: inset 3px 0 0 var(--ads-danger-bg, #C73945);
}

/* --- Field + label ---------------------------------------------------------- */
.field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  position: relative;
}
/* Row-spanning field modifier. Applied when a single .field needs to
 * occupy the entire .create-form__row (or --three) width instead of a
 * single grid cell. Used by the consolidated Line conditions input on
 * the LINE form (2026-07-09) so the recovered horizontal space from
 * dropping Line condition 2/3 backs one wider comma-separated input
 * rather than leaving empty grid slots. */
.field--full {
  grid-column: 1 / -1;
}
.field__label {
  /* Figma 70:1681 etc. — label text uses muted gray (Gray/70 #52637380
   * or thereabouts), NOT the primary dark text. Weight is regular 400. */
  font-family: "InspireTWDC", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 21px;
  font-weight: 400;
  color: var(--gray-70);
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
}
.field__req {
  color: var(--indigo-70);
  font-weight: 700;
}
.field__optional {
  color: var(--gray-50);
  font-weight: 400;
  font-size: 14px;
}
/* Info icon next to label - Figma 63:216 sub-component "info" is 12x12
 * (node 85:2, 85:24, etc. all consistent). The SVG inside is also 12x12.
 * Color is the same muted tone as the label (text/secondary #3B4347). */
.field__help {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 12px;
  height: 12px;
  color: var(--gray-50);
  cursor: help;
  /* role="button" + tabindex="0" on the trigger lets keyboard users
   * tab to the helper icon and see the ADS tooltip on focus. The
   * focus ring matches the ADS Indigo brand. */
  border-radius: 50%;
}
.field__help svg { width: 12px; height: 12px; display: block; }
.field__help:hover { color: var(--indigo-70); }
.field__help:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: 2px;
  color: var(--indigo-70);
}
.theme-ads .field__help { color: var(--ads-text-secondary); }
.theme-ads .field__help:hover { color: var(--ads-brand); }
.theme-ads .field__help:focus-visible {
  outline-color: var(--ads-brand);
  color: var(--ads-brand);
}
.field__input {
  height: 40px;
  padding: 9.5px 12px;
  background: #FFFFFF;
  border: 1px solid var(--gray-20);
  border-radius: 6px;
  font-family: "InspireTWDC", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 21px;
  color: var(--gray-95);
  box-sizing: border-box;
  width: 100%;
  outline: none;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.field__input::placeholder { color: var(--placeholder-color); opacity: 1; }
.field__input:hover { border-color: var(--gray-30); }
.field__input:focus,
.field__input:focus-visible {
  border-color: var(--indigo-70);
  box-shadow: 0 0 0 2px rgba(54, 17, 200, 0.16);
}
.field__input--readonly {
  background: var(--gray-05);
  color: var(--gray-50);
  cursor: not-allowed;
}
.field__input--readonly:hover { border-color: var(--gray-20); }
/* Base disabled state (used in EDL theme). theme-ads overrides this
 * with the canonical ADS disabled tokens; here we just ensure the EDL
 * theme also gets a no-focus / muted-placeholder treatment. */
.field__input[disabled],
.field__input[disabled]:hover,
.field__input[disabled]:focus {
  background: var(--gray-05);
  color: var(--gray-50);
  cursor: not-allowed;
  box-shadow: none;
}
.field__input[disabled]::placeholder { color: var(--placeholder-color); opacity: 1; }
/* ----------------------------------------------------------------------
 * Validation error row (icon + "Required" text).
 *
 * Same layout in every field: icon left, text right, 6px gap, both
 * vertically centered on a 16px line. The offset from the input's
 * bottom border to the error text top is always 8px, regardless of
 * whether the field wraps a plain <input> (error is a direct child of
 * .field, which is display:flex column with 4px gap) or a nested
 * .edl-select / .ads-datepicker wrapper (no parent flex gap). The
 * two branches below add the necessary margin-top so the visible
 * offset is 8px in both cases.
 *
 * Icon is a real inline SVG injected by setFieldError() in app.js,
 * NOT a CSS ::before mask. This lets the 16x16 render survive font-
 * size / line-height changes and inherits color via currentColor. */
.field__error {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: "InspireTWDC", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 20px;
  color: #C8242D;
  margin-top: 0;
}
/* Direct-child of .field: .field's own flex `gap: 4px` already
 * contributes 4px between the input and the error, so we only need
 * an extra 4px of margin-top to reach the 8px spec. */
.field > .field__error {
  margin-top: 4px;
}
/* Nested-wrapper case (Marketplace, Deal Season, Effective start/end
 * date, etc.): the error is inside .edl-select / .ads-datepicker,
 * which don't have flex gap. Full 8px margin-top here. */
.edl-select > .field__error,
.ads-datepicker > .field__error {
  margin-top: 8px;
}
.field__error-icon {
  /* Locked 16x16 render. flex-shrink:0 prevents narrow flex parents
   * (e.g. tight grid cells at 1024px width) from squishing the
   * warning glyph. Color follows currentColor from .field__error. */
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  display: block;
  color: currentColor;
}
.field__error-text {
  /* Line-height matches the icon height so the row is exactly 16px
   * and never reserves extra height above or below. */
  line-height: 16px;
  color: inherit;
}
.field.is-invalid .field__input,
.field.is-invalid .edl-select__trigger,
.field.is-invalid .ads-datepicker__trigger {
  border-color: #C8242D;
}
.field.is-invalid .field__input:focus,
.field.is-invalid .edl-select__trigger:focus-visible,
.field.is-invalid .ads-datepicker__trigger:focus-visible {
  box-shadow: 0 0 0 2px rgba(200, 36, 45, 0.16);
}

/* ============================================================================
 * EDL DROPDOWN  (Marketplace + Deal Season)
 * Figma node 15094:65030 — menu surface: white, 8px radius, drop shadow
 * `0 0 2px rgba(23,29,35,0.25)`. Option text Indigo/70 #3611C8, font
 * InspireTWDC Roman 14/21, padding 16px x / 8px y per option.
 * Trigger reuses .field__input chrome so it matches every other field.
 * ============================================================================ */
.edl-select { position: relative; }
.edl-select__trigger {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  text-align: left;
  cursor: pointer;
  user-select: none;
  background: #FFFFFF;
}
.edl-select__trigger[aria-expanded="true"] {
  border-color: var(--indigo-70);
  box-shadow: 0 0 0 2px rgba(54, 17, 200, 0.16);
}

/* ---- Compact variant: footer page-size selector ---------------------
 * The form-field default trigger is 40h with form-input padding -
 * too tall for the table footer's 32h compact row. This modifier
 * shrinks the trigger to 32h x ~64w, tight padding (0/8 left/right
 * minus chevron space), and switches the chevron to be inline at
 * the same 16px size used elsewhere.
 *
 * The menu chrome (border, radius, shadow, hover, selected states)
 * uses the same .edl-select__menu + .edl-select__option rules as
 * the full-size selects - only the trigger needs to scale down. */
.edl-select--compact { display: inline-flex; }
/* Higher-specificity overrides for the trigger so the compact
 * variant beats .field__input's 40h default. Without the explicit
 * height + min-height + box-sizing, the form-input chrome would
 * stretch the footer pill to 40px. */
.edl-select--compact .edl-select__trigger.field__input,
.edl-select--compact .edl-select__trigger {
  min-width: 64px;
  width: 64px;
  height: 32px;
  min-height: 32px;
  padding: 0 8px;
  gap: 4px;
  border: 1px solid #B6C4D3;
  border-radius: 8px;
  font-family: "InspireTWDC", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 21px;
  color: var(--gray-95);
  box-sizing: border-box;
}
.edl-select--compact .edl-select__value {
  /* Single short token like "10" / "20" / "50" - just center-align
   * the small label inside the pill so it doesn't shift left when
   * the user picks a 2-digit value. */
  text-align: left;
}
/* In ADS theme the compact trigger inherits Open Sans + the ADS
 * input chrome (rgba(15,18,20,0.20) border + 6px radius) so it
 * matches the rest of the ADS form controls in the table footer. */
.theme-ads .edl-select--compact .edl-select__trigger {
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  line-height: 20px;
  border-color: var(--ads-border-default);
  border-radius: 6px;
  color: var(--ads-text-primary);
}

/* ---- Focus / open state for the compact footer dropdowns ----------------
 * The form-input default focus treatment adds a 2px soft indigo
 * box-shadow (`.field__input:focus-visible` and the bare
 * `.edl-select__trigger[aria-expanded="true"]` rule above). On the
 * 32px compact pills used in the table footer that wash reads as a
 * pale "halo" sitting against the Gray-05 page background - users
 * reported it as a "white halo / unintended wrapper around the
 * dropdown" (2026-06-25 brief).
 *
 * The canonical ADS focus pattern is a 2px brand-color border swap
 * with NO box-shadow (see --ads-input-border-active = 2px,
 * --ads-input-border-active-color = ads-brand). Apply that to the
 * compact triggers in both themes so the focus / open affordance is
 * a crisp 2px Indigo outline of the same shape as the button - no
 * extra blurry/lavender halo bleeding past the button edge.
 *
 * Padding is reduced by 1px on focus so the +1px border doesn't
 * shift the inner text. Outline stays `none` because the border IS
 * the focus indicator (matches the full-size ADS field treatment).
 * The compact pill stays vertically centered at 32px because we
 * absorb the extra border into padding.
 * ---------------------------------------------------------------------- */
.edl-select--compact .edl-select__trigger:focus,
.edl-select--compact .edl-select__trigger:focus-visible,
.edl-select--compact .edl-select__trigger[aria-expanded="true"] {
  border-width: 2px;
  border-color: var(--indigo-70);
  padding: 0 7px;       /* was 0 8px; absorb the +1px border */
  box-shadow: none;     /* kill the soft 2px wash halo */
  outline: none;
}
.theme-ads .edl-select--compact .edl-select__trigger:focus,
.theme-ads .edl-select--compact .edl-select__trigger:focus-visible,
.theme-ads .edl-select--compact .edl-select__trigger[aria-expanded="true"] {
  border-color: var(--ads-input-border-active-color, var(--ads-brand));
  border-width: var(--ads-input-border-active, 2px);
  padding: 0 7px;
  box-shadow: none;
  outline: none;
}
.edl-select__value {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--gray-95);
}
.edl-select__value--placeholder { color: var(--placeholder-color); }
.edl-select__chev {
  color: var(--gray-70);
  flex: 0 0 16px;
  transition: transform 120ms ease;
}
.edl-select__trigger[aria-expanded="true"] .edl-select__chev {
  transform: rotate(180deg);
  color: var(--indigo-70);
}
/* The menu lives in DOM as a child of .edl-select but is PORTALED to
 * document.body when opened (toggleEdlSelect -> _edlPositionMenu).
 * In its portaled state JS sets position:fixed + top/left/width
 * inline so the menu floats above the page regardless of any
 * overflow:hidden / transform / containment ancestor (accordion
 * cards have overflow:hidden which would otherwise clip the menu).
 *
 * The rules below are the SHARED chrome - they apply equally well
 * when the menu is in its DOM home (position:absolute, used as a
 * fallback for any select not yet wired through the portal) and
 * when it's been moved to body (position:fixed, set inline). */
.edl-select__menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  max-height: 280px;
  overflow-y: auto;
  margin: 0;
  padding: 8px 0;
  list-style: none;
  background: #FFFFFF;
  border: 1px solid var(--gray-20);
  border-radius: 8px;
  box-shadow: 0 0 2px rgba(23, 29, 35, 0.25), 0 4px 12px rgba(23, 29, 35, 0.08);
  z-index: 1150;
}
/* min-width:100% applies ONLY when the menu sits inside its
 * .edl-select wrapper. When portaled to document.body the menu
 * receives an explicit inline width = trigger width (set by
 * _edlPositionMenu) and must not stretch to the body width. */
.edl-select > .edl-select__menu { min-width: 100%; }
.edl-select__option {
  display: block;
  padding: 8px 16px;
  font-family: "InspireTWDC", "DM Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 21px;
  font-weight: 400;
  color: var(--indigo-70);        /* per node 15094:65030 spec */
  cursor: pointer;
  user-select: none;
}
.edl-select__option:hover,
.edl-select__option.is-active {
  background: var(--indigo-5);
}
.edl-select__option.is-selected {
  font-weight: 700;
  background: var(--indigo-10);
}

/* ============================================================================
 * ADS CALENDAR  (Effective Start + End)
 * Figma node 2697:214 — popover chrome inherits ADS Popover tokens: 6px
 * radius, 1px border rgba(15,18,20,0.2), elevation/30 shadow. Inside, dates
 * are a 7-column flex grid; today is ringed in brand, selected gets brand
 * fill + white text. We override the brand color to EDL Indigo/70 so the
 * calendar reads as "EDL form using ADS interaction". Header is bold for
 * the month label with chevron prev/next.
 * ============================================================================ */
.ads-datepicker { position: relative; }
.ads-datepicker__trigger {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  cursor: pointer;
  user-select: none;
  text-align: left;
  background: #FFFFFF;
}
.ads-datepicker__trigger[aria-expanded="true"] {
  border-color: var(--indigo-70);
  box-shadow: 0 0 0 2px rgba(54, 17, 200, 0.16);
}
.ads-datepicker__value {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--gray-95);
}
/* Empty-state placeholder treatment for date pickers - mirrors the
 * edl-select__value--placeholder pattern. Applied when the datepicker
 * has no selected value, so the placeholder copy reads as muted. */
.ads-datepicker__value--placeholder { color: var(--placeholder-color); }
.theme-ads .ads-datepicker__value--placeholder { color: var(--ads-text-placeholder); }
.ads-datepicker__icon {
  color: var(--gray-70);
  flex: 0 0 16px;
}
.ads-datepicker__trigger[aria-expanded="true"] .ads-datepicker__icon { color: var(--indigo-70); }

/* Popover - portal-style: position:fixed so it escapes every
 * overflow:hidden ancestor (.surface, .accordion__body, etc.). Top/left
 * are computed by JS from the trigger's getBoundingClientRect() each
 * time the popover opens (and the popover is appended to document.body
 * so even ancestors with transform/filter can't trap it).
 *
 * z-index 1150 sits above the modal scrim (1000), the Quick Edit sheet
 * (1100), and below the tooltip (1300), so date pickers always render
 * above other UI but never cover a tooltip. */
.ads-datepicker__popover {
  position: fixed;
  top: 0;
  left: 0;
  width: 283px;
  background: var(--ads-popover-bg, #FFFFFF);
  border: 1px solid var(--ads-popover-border, var(--gray-20));
  border-radius: var(--ads-popover-radius, 6px);
  box-shadow: var(--ads-popover-shadow, 0 4px 12px rgba(15, 18, 20, 0.30));
  padding: var(--ads-popover-pad, 16px);
  z-index: 1150;
  font-family: "InspireTWDC", "DM Sans", Inter, sans-serif;
  box-sizing: border-box;
}
/* Flip-up class kept for backward compat but no longer needed since JS
 * now computes the exact top coordinate per orientation. Leaving as a
 * no-op so any historical CSS selectors don't break. */
.ads-datepicker--flip-up .ads-datepicker__popover { /* positioning handled in JS */ }
.ads-cal {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* Calendar header: prev + month label + next sit as a centered cluster
 * with a tight 4px gap between each. Using justify-content: center +
 * a small gap pulls both chevrons in close to the month label instead
 * of pinning them to the popover edges. Padding stays at 4px y to keep
 * the popover at the ADS-compliant overall height. */
.ads-cal__header {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 4px 0;
  color: var(--gray-95);
}
.ads-cal__header .ads-cal__title {
  flex: 0 0 auto;
  text-align: center;
}
.ads-cal__nav {
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: 4px;
  cursor: pointer;
  color: var(--gray-70);
  font: inherit;
}
.ads-cal__nav:hover { background: var(--gray-05); color: var(--gray-95); }
.ads-cal__nav:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: -2px;
}
/* Chevron icon - 16x16 to match the date-picker trigger icon scale.
 * display:block prevents inline baseline gap inside the 28px button. */
.ads-cal__navicon { display: block; width: 16px; height: 16px; }
.ads-cal__title {
  font-weight: 700;
  font-size: 14px;
  line-height: 20px;
  color: var(--gray-95);
}
.ads-cal__weekdays,
.ads-cal__grid {
  /* ADS Date Picker uses 2px y-gap, 0 x-gap between day cells (Figma
   * I2697:214;2693:21;684:2841 gap-[2px_0px]). The 7-column grid
   * naturally distributes day cells evenly across the popover width. */
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px 0;
}
.ads-cal__weekday {
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  line-height: 20px;
  color: var(--gray-50);
}
.ads-cal__day {
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  font-family: inherit;
  font-size: 14px;
  line-height: 20px;
  color: var(--gray-95);
  cursor: pointer;
  padding: 0;
}
.ads-cal__day:hover {
  background: var(--indigo-5);
  color: var(--indigo-70);
}
.ads-cal__day.is-out-of-month { color: rgba(15, 18, 20, 0.30); }
.ads-cal__day.is-today {
  /* Today = brand ring (per ADS spec: today is ringed indigo). */
  border-color: var(--indigo-70);
  color: var(--indigo-70);
  font-weight: 600;
}
.ads-cal__day.is-selected {
  /* Selected = brand fill + white text. EDL Indigo/70 override of ADS brand. */
  background: var(--indigo-70);
  color: #FFFFFF;
  border-color: var(--indigo-70);
  font-weight: 600;
}
.ads-cal__day:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: 1px;
}

/* ============================================================================
 * CREATE PAGE — responsive
 * Stack fields below 760, collapse 3-up below 600, panels stay in viewport.
 * ============================================================================ */
@media (max-width: 1099px) {
  /* Header gutter mirrors .page__content's narrow-viewport gutter so
   * the topwrap inner content + .surface cards still share edges.
   * .create__topwrap keeps its own vertical padding (12 top / 18
   * bottom); we just bump the outer .page__top gutter. */
  [data-page="create"] .page__top,
  [data-page="line"]   .page__top { padding-left: 32px; padding-right: 32px; }
  [data-page="create"] .page__content,
  [data-page="line"]   .page__content { padding-left: 32px; padding-right: 32px; }
  .create__topwrap { padding-top: 24px; }
  .create__card { padding: 24px; }
}
@media (max-width: 899px) {
  .create__header { flex-wrap: wrap; }
  .create__actions { width: 100%; margin-top: 8px; }
}
@media (max-width: 759px) {
  .create-form__row,
  .create-form__row--three {
    grid-template-columns: 1fr;
    gap: 16px;
  }
}
@media (max-width: 600px) {
  [data-page="create"] .page__top,
  [data-page="line"]   .page__top { padding-left: 16px; padding-right: 16px; }
  [data-page="create"] .page__content,
  [data-page="line"]   .page__content { padding-left: 16px; padding-right: 16px; }
  .create__topwrap { padding-top: 20px; }
  .create__card { padding: 16px; }
  .stepper { gap: 12px; flex-wrap: wrap; row-gap: 8px; }
  .stepper__step + .stepper__step::before { width: 24px; margin-right: 4px; flex-basis: 24px; }
  /* On the LINE step the count chips make each step ~140px wide; with 3
   * steps that's >600px without connectors. Hide chips ≤600 so the stepper
   * fits, and shorten / hide connectors so steps can wrap to 2 rows. */
  [data-page="line"] .stepper__chip { display: none; }
  /* Calendar popover may otherwise overflow on tiny screens — cap at viewport */
  .ads-datepicker__popover { width: min(280px, calc(100vw - 32px)); }
}
@media (max-width: 480px) {
  /* Even without chips, stepper labels at 14px push width — drop labels
   * smaller and let connectors shrink to keep the row visible. */
  [data-page="line"] .stepper__label,
  [data-page="create"] .stepper__label { font-size: 14px; }
  .stepper__step + .stepper__step::before { width: 12px; flex-basis: 12px; margin: 0 4px; }
}

/* ============================================================================
 * ADS PREVIEW — Create page overrides
 * Same product, ADS surface. Type swaps to Open Sans; brand fill/text uses
 * ADS indigo #4045C2; chip/option color shifts; menu chrome inherits ADS
 * popover tokens (1px 20% black border + elevation/30 shadow).
 * ============================================================================ */
.theme-ads .create__back { font-family: "Open Sans", sans-serif; color: var(--ads-brand); }
.theme-ads .stepper__step,
/* --- ADS Field / Input / Select / Date Picker (Figma compliance pass)
 *
 * Every form control on the page (text input, select trigger, date
 * picker trigger) shares one set of tokens: 36px height, 6px radius,
 * Open Sans 14/20 body text, 1px rest border / 2px active border,
 * label = Open Sans SemiBold 12/18. We pin every value to a token so
 * a future ADS update means changing the token, not hunting down rules.
 * Component IDs: Input 63:216, Select 2551:55, Date Picker 687:2824.
 * ----------------------------------------------------------------- */

/* Shared typography */
.theme-ads .field__label,
.theme-ads .field__optional,
.theme-ads .field__input,
.theme-ads .edl-select__option,
.theme-ads .edl-select__value,
.theme-ads .ads-datepicker__value,
.theme-ads .create-form__sectiontitle,
.theme-ads .create-form__sectionhelp,
.theme-ads .ads-cal,
.theme-ads .ads-datepicker__popover {
  font-family: "Open Sans", "DM Sans", -apple-system, BlinkMacSystemFont,
    "Segoe UI", Helvetica, Arial, sans-serif;
}
.theme-ads .field__req { color: var(--ads-brand); }

/* Field label - Figma "field-description" (12/18 SemiBold #3B4347) */
.theme-ads .field__label {
  font-size: var(--ads-label-size);
  line-height: var(--ads-label-line);
  font-weight: 600;
  color: var(--ads-label-color);
}
.theme-ads .field__optional {
  font-size: var(--ads-label-size);
  line-height: var(--ads-label-line);
  font-weight: 400;
  color: var(--ads-text-tertiary);
}
.theme-ads .field { gap: var(--ads-label-gap); }
/* Date picker field uses a smaller label-trigger gap (4px) per Figma
 * Date Picker spec - tighter than Input/Select which use 8px. Apply
 * scoped to .field containing a .ads-datepicker so other fields are
 * unaffected. :has() is supported in all modern browsers (2023+). */
.theme-ads .field:has(.ads-datepicker) { gap: var(--ads-label-gap-datepicker); }

/* In theme-ads the parent `.field` flex gap is already 8px, which is
 * the entire input-to-error offset we want. The base rule adds a
 * further 4px margin-top so plain themes reach 8px total; cancel
 * that here so theme-ads doesn't end up at 12px. Nested wrappers
 * (.edl-select / .ads-datepicker) are unaffected because their
 * .field__error is a child of the wrapper, not of .field. */
.theme-ads .field > .field__error {
  margin-top: 0;
}

/* Input - 36h, 8/12 pad, 6r, 14/20 Regular, 1px rest border, 2px active */
.theme-ads .field__input {
  height: var(--ads-input-height);
  padding: var(--ads-input-pad-y) var(--ads-input-pad-x);
  border-width: var(--ads-input-border-width);
  border-style: solid;
  border-color: var(--ads-input-border-rest);
  border-radius: var(--ads-input-radius);
  background: var(--ads-surface);
  color: var(--ads-input-text);
  font-size: 14px;
  line-height: 20px;
  font-weight: 400;
}
.theme-ads .field__input::placeholder {
  color: var(--ads-input-placeholder);
  font-weight: 400;
  opacity: 1;
}
.theme-ads .field__helper {
  color: var(--ads-text-tertiary);
  font: 400 var(--ads-label-size)/var(--ads-label-line) "Open Sans", sans-serif;
}
.theme-ads .field__input:-webkit-autofill,
.theme-ads .field__input:-webkit-autofill:hover,
.theme-ads .field__input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--ads-input-text);
  caret-color: var(--ads-input-text);
  box-shadow: 0 0 0 1000px var(--ads-surface) inset;
  transition: background-color 9999s ease-out 0s;
}
.theme-ads .field__input:hover {
  border-color: var(--ads-input-border-hover);
}
.theme-ads .field__input:focus,
.theme-ads .field__input:focus-visible {
  /* ADS active = 2px solid brand border, NO ring shadow */
  border-width: var(--ads-input-border-active);
  border-color: var(--ads-input-border-active-color);
  /* Compensate for the +1px border so the field doesn't shift */
  padding: calc(var(--ads-input-pad-y) - 1px) calc(var(--ads-input-pad-x) - 1px);
  box-shadow: none;
}
.theme-ads .field__input--readonly,
.theme-ads .field__input--readonly:hover {
  background: var(--ads-input-bg-disabled);
  color: var(--ads-input-text-disabled);
  border-color: var(--ads-input-border-disabled);
  cursor: default;
}
.theme-ads .field__input[readonly],
.theme-ads .field__input[readonly]:hover {
  background: var(--ads-surface);
  color: var(--ads-input-text);
  border-color: var(--ads-input-border-rest);
  cursor: default;
}
.theme-ads .field__input[readonly]:focus,
.theme-ads .field__input[readonly]:focus-visible {
  border-width: var(--ads-input-border-active);
  border-color: var(--ads-input-border-active-color);
  padding: calc(var(--ads-input-pad-y) - 1px) calc(var(--ads-input-pad-x) - 1px);
}
/* ADS Disabled Input (Figma 63:101). Per the spec the disabled state
 * KEEPS the rest background (white) and rest padding/radius - only the
 * border-color and text-color drop to muted treatments. The native
 * [disabled] attribute already suppresses focus, typing, hover-edit,
 * and form submission; the styles below enforce ADS visuals on top of
 * the browser default. */
.theme-ads .field__input[disabled],
.theme-ads .field__input[disabled]:hover,
.theme-ads .field__input[disabled]:focus,
.theme-ads .field__input[disabled]:focus-visible,
.theme-ads .field__input[disabled]:active {
  background: var(--ads-input-bg-disabled);            /* white */
  border-width: var(--ads-input-border-width);         /* 1px (no 2px focus shift) */
  border-color: var(--ads-input-border-disabled);      /* rgba(15,18,20,0.05) */
  color: var(--ads-input-text-disabled);               /* rgba(15,18,20,0.30) */
  padding: var(--ads-input-pad-y) var(--ads-input-pad-x); /* 8/12 - undo focus pad compensation */
  cursor: not-allowed;
  box-shadow: none;
  outline: none;
}
/* Placeholder text uses the same muted disabled color (Figma 63:107
 * shows the placeholder/value in field-description/state/disabled/text).
 * Browsers don't inherit color into ::placeholder, so it has to be set
 * explicitly. Standard + Webkit + Mozilla prefixed selectors. */
.theme-ads .field__input[disabled]::placeholder {
  color: var(--ads-input-text-disabled);
  opacity: 1;                                          /* override Firefox 54% default */
}
.theme-ads .field__input[disabled]::-webkit-input-placeholder { color: var(--ads-input-text-disabled); opacity: 1; }
.theme-ads .field__input[disabled]::-moz-placeholder { color: var(--ads-input-text-disabled); opacity: 1; }
/* Make sure the user-select doesn't allow accidental text-drag editing */
.theme-ads .field__input[disabled] { user-select: none; -webkit-user-select: none; }
.theme-ads .ads-datepicker__trigger[aria-disabled="true"],
.theme-ads .ads-datepicker__trigger[aria-disabled="true"]:hover {
  background: var(--ads-input-bg-disabled);
  border-color: var(--ads-input-border-disabled);
  color: var(--ads-input-text-disabled);
  cursor: not-allowed;
}
.theme-ads .ads-datepicker__trigger[aria-disabled="true"] .ads-datepicker__value,
.theme-ads .ads-datepicker__trigger[aria-disabled="true"] .ads-datepicker__icon {
  color: var(--ads-input-text-disabled);
}
.theme-ads .ads-datepicker__trigger[aria-readonly="true"],
.theme-ads .ads-datepicker__trigger[aria-readonly="true"]:hover {
  background: var(--ads-surface);
  border-color: var(--ads-input-border-rest);
  color: var(--ads-input-text);
  cursor: default;
}
.theme-ads .ads-datepicker__trigger[aria-readonly="true"] .ads-datepicker__value {
  color: var(--ads-input-text);
}

/* Error state - red border + red error text (#DB5461 border, #993B44 text) */
.theme-ads .field.is-invalid .field__input,
.theme-ads .field.is-invalid .edl-select__trigger,
.theme-ads .field.is-invalid .ads-datepicker__trigger {
  border-width: var(--ads-input-border-width);
  border-color: var(--ads-input-border-error);
  /* Compensate the focus-state padding shift so error state matches rest height */
  padding: var(--ads-input-pad-y) var(--ads-input-pad-x);
  box-shadow: none;
}
.theme-ads .field.is-invalid .field__input:focus,
.theme-ads .field.is-invalid .edl-select__trigger[aria-expanded="true"],
.theme-ads .field.is-invalid .ads-datepicker__trigger[aria-expanded="true"] {
  border-width: var(--ads-input-border-active);
  border-color: var(--ads-input-border-error);
  padding: calc(var(--ads-input-pad-y) - 1px) calc(var(--ads-input-pad-x) - 1px);
  box-shadow: none;
}
/* Error message - per Figma 63:134 Error Message anatomy:
 *   16x16 warning-circle icon + 12/16 Open Sans Regular text in
 *   #993B44 (field-validation/type/error/text). 6px gap between
 *   icon and text, per the 2026-07-05 spacing/icon fix brief.
 *   Layout (display/gap/align) lives in the base .field__error
 *   rule; theme-ads only re-skins color/font. The 16x16 icon is a
 *   real inline SVG (.field__error-icon) injected by setFieldError()
 *   in app.js, so its size can't be shrunk by inherited font-size. */
.theme-ads .field__error {
  color: var(--ads-input-error-text);
  font-family: "Open Sans", sans-serif;
  font-size: var(--ads-label-size);
  line-height: var(--ads-label-line);
  font-weight: 400;
}
/* Error rows are only inserted into the field flex stack when the field
 * is invalid; otherwise [hidden] keeps them out of flow entirely. */
.theme-ads .field__error[hidden] { display: none; }
.theme-ads .stepper__step--active .stepper__dot { background: var(--ads-brand); }
/* Select trigger - shares input chrome but with 6y padding (per Figma) */
.theme-ads .edl-select__trigger {
  padding: var(--ads-select-pad-y) var(--ads-input-pad-x);
  border-color: var(--ads-input-border-rest);
  background: var(--ads-surface);
}
.theme-ads .edl-select__trigger:hover {
  border-color: var(--ads-input-border-hover);
}
.theme-ads .edl-select__value {
  color: var(--ads-text-primary);
  font-size: 14px;
  line-height: 20px;
  font-weight: 400;
}
.theme-ads .edl-select__value--placeholder { color: var(--ads-input-placeholder); }
.theme-ads .edl-select__chev { color: var(--ads-text-tertiary); }
.theme-ads .edl-select__trigger[aria-expanded="true"] {
  border-width: var(--ads-input-border-active);
  border-color: var(--ads-input-border-active-color);
  padding: calc(var(--ads-select-pad-y) - 1px) calc(var(--ads-input-pad-x) - 1px);
  box-shadow: none;
}
.theme-ads .edl-select__trigger[aria-expanded="true"] .edl-select__chev {
  color: var(--ads-brand);
}

/* Select popover menu - elevation/30 shadow, 6r, white surface.
 * Per Figma 2551:31 (Select State=Open) the menu has 0 inner padding -
 * option rows stack flush with the menu edges. The dropdown chrome is
 * the popover/* tokens, the option chrome is the select-option/* tokens. */
.theme-ads .edl-select__menu {
  border-radius: var(--ads-popover-radius);
  border-color: var(--ads-popover-border);
  background: var(--ads-popover-bg);
  box-shadow: var(--ads-popover-shadow);
  padding: 0;
  overflow: hidden;        /* clip first/last option corners to menu radius */
}

/* Select option - 36h, 8/12 pad, Open Sans 14/20 text-primary */
.theme-ads .edl-select__option {
  height: var(--ads-option-height);
  padding: var(--ads-option-pad-y) var(--ads-option-pad-x);
  font-size: 14px;
  line-height: 20px;
  font-weight: 400;
  color: var(--ads-text-primary);          /* not brand indigo - text-primary */
  display: flex;
  align-items: center;
  gap: var(--ads-option-gap);
  background: transparent;
}
.theme-ads .edl-select__option:hover,
.theme-ads .edl-select__option.is-active {
  background: var(--ads-option-hover-bg);
}
.theme-ads .edl-select__option.is-selected {
  background: var(--ads-option-selected-bg);
  color: var(--ads-text-primary);
  font-weight: 400;                        /* not bold - selected uses check icon */
}
.theme-ads .edl-select__option.is-selected::before {
  content: "";
  display: inline-block;
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
  background-color: currentColor;
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'><path d='M13.5 4.5L6.5 11.5L2.5 7.5' stroke='currentColor' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'><path d='M13.5 4.5L6.5 11.5L2.5 7.5' stroke='currentColor' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}
/* Non-selected options need a 16px spacer so labels line up under the
 * check icon. Inserted via ::before placeholder. */
.theme-ads .edl-select__option:not(.is-selected)::before {
  content: "";
  display: inline-block;
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
}

/* Date Picker trigger - same chrome as Input */
.theme-ads .ads-datepicker__trigger {
  padding: var(--ads-input-pad-y) var(--ads-input-pad-x);
  border-color: var(--ads-input-border-rest);
  background: var(--ads-surface);
  height: var(--ads-input-height);
}
.theme-ads .ads-datepicker__trigger:hover {
  border-color: var(--ads-input-border-hover);
}
.theme-ads .ads-datepicker__value {
  color: var(--ads-text-primary);
  font-size: 14px;
  line-height: 20px;
  font-weight: 400;
}
.theme-ads .ads-datepicker__value--placeholder { color: var(--ads-input-placeholder); }
.theme-ads .ads-datepicker__icon { color: var(--ads-text-tertiary); }
.theme-ads .ads-datepicker__trigger[aria-expanded="true"] {
  border-width: var(--ads-input-border-active);
  border-color: var(--ads-input-border-active-color);
  padding: calc(var(--ads-input-pad-y) - 1px) calc(var(--ads-input-pad-x) - 1px);
  box-shadow: none;
}
.theme-ads .ads-datepicker__trigger[aria-expanded="true"] .ads-datepicker__icon { color: var(--ads-brand); }
.theme-ads .ads-datepicker__trigger[aria-disabled="true"],
.theme-ads .ads-datepicker__trigger[aria-disabled="true"]:hover {
  background: var(--ads-input-bg-disabled);
  border-color: var(--ads-input-border-disabled);
  color: var(--ads-input-text-disabled);
}
.theme-ads .ads-datepicker__trigger[aria-readonly="true"],
.theme-ads .ads-datepicker__trigger[aria-readonly="true"]:hover {
  background: var(--ads-surface);
  border-color: var(--ads-input-border-rest);
  color: var(--ads-input-text);
}

/* Date Picker popover - 6r, elevation/30 shadow, 16px padding */
.theme-ads .ads-datepicker__popover {
  border-radius: var(--ads-popover-radius);
  border: 1px solid var(--ads-popover-border);
  background: var(--ads-popover-bg);
  box-shadow: var(--ads-popover-shadow);
  padding: var(--ads-popover-pad);
  width: 283px;                              /* Figma Date Picker width */
}
.theme-ads .ads-cal__title {
  font-family: "Open Sans", sans-serif;
  font-weight: 700;                          /* Open Sans Bold per Figma */
  font-size: 14px;                           /* font/body/normal/sm */
  line-height: 18px;
}
.theme-ads .ads-cal__nav {
  color: var(--ads-text-tertiary);
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  line-height: 18px;
}
.theme-ads .ads-cal__nav:hover {
  background: var(--ads-surface-hover);
  color: var(--ads-text-primary);
}
.theme-ads .ads-cal__weekday {
  font-family: "Open Sans", sans-serif;
  font-size: 14px;                           /* font/body/normal/xs */
  line-height: 20px;
  color: var(--ads-text-tertiary);
  height: var(--ads-day-cell-height);
}
.theme-ads .ads-cal__day {
  height: var(--ads-day-cell-height);
  border-radius: var(--ads-radius-day);
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  line-height: 20px;
  font-weight: 400;
  color: var(--ads-text-primary);
}
.theme-ads .ads-cal__day.is-out-of-month { color: var(--ads-text-disabled); }
.theme-ads .ads-cal__day:hover {
  background: var(--ads-surface-hover);
  color: var(--ads-text-primary);
}
.theme-ads .ads-cal__day.is-today {
  border: 1px solid var(--ads-brand);
  border-radius: var(--ads-radius-day-active);
  color: var(--ads-text-primary);
  font-weight: 600;
  background: transparent;
}
.theme-ads .ads-cal__day.is-selected {
  background: var(--ads-brand);
  border: 1px solid var(--ads-brand);
  border-radius: var(--ads-radius-day-active);
  color: var(--ads-text-on-primary);
  font-weight: 400;
}
.theme-ads .ads-cal__day:focus-visible {
  outline: 2px solid var(--ads-brand);
  outline-offset: 1px;
}
/* (Older duplicate .theme-ads .ads-cal__day rules removed in the ADS
 * compliance pass - the canonical rules live above and use ADS tokens.) */

/* ============================================================================
 * RESPONSIVE LAYER
 * ============================================================================
 *
 * Strategy (per spec):
 *   - Desktop layout (≥1440) stays 1:1 with Figma. None of the rules below
 *     affect the desktop visual fidelity.
 *   - The TABLE has a locked min-width matching the Figma desktop natural
 *     width (1304px). Only the table area scrolls horizontally — the page
 *     itself never gets a horizontal scrollbar.
 *   - The page header and toolbar use flex-wrap so primary actions remain
 *     visible and clickable when the viewport gets narrow.
 *   - The filter side panel caps at viewport width so Apply / Cancel
 *     never disappear off-screen.
 *
 * Breakpoints used:
 *     ≥1440px  — Figma desktop (no changes)
 *      <1440px — Table card stays 100% of content width; table scrolls inside
 *      <900px  — Page header actions wrap below the title
 *      <700px  — Toolbar (Filter+Search) stacks vertically;
 *                Pagination footer wraps to multi-line
 *      <600px  — Filter side panel becomes full-viewport-wide
 * ========================================================================== */

/* --- Page shell guard --------------------------------------------------------
 * The single hard rule: the page must NEVER scroll horizontally because of
 * the table. The table's overflow lives inside .table-scroll only.
 *
 * `overflow-x: clip` is preferred over `hidden` on the root scroll host so
 * that `position: sticky` on the .gnav top bar keeps working. Per the CSS
 * Overflow spec, `overflow-x: hidden` combined with `overflow-y: visible`
 * causes overflow-y to compute to `auto`, which turns html/body into a
 * scroll container that captures sticky positioning and defeats it. The
 * `clip` value clips overflow WITHOUT establishing a scroll container, so
 * the viewport remains the sticky ancestor and .gnav sticks to the top of
 * the browser viewport during page scroll (2026-07-09 sticky-header brief).
 * The `hidden` line is retained above `clip` as a fallback for very old
 * browsers that don't understand `clip` (Chrome <90, Safari <16). */
html, body {
  overflow-x: hidden;
  overflow-x: clip;
}

/* --- Table wrapper (horizontal scroll owned by this element) ---------
 * .table-scroll wraps the table grid and owns the horizontal
 * scrollbar for narrow viewports. v1.1 keeps its 2026-07-08 behavior
 * (overflow-x: hidden, no horizontal scrolling) because its column
 * template collapses Name to absorb overflow. v1.2 opts in to
 * horizontal scroll (see the "Horizontal scroll + sticky columns"
 * block below) so all columns stay reachable through the scrollbar
 * while Status, Rate Card ID, Name (left) and Action (right) stay
 * pinned in place. The page shell (html, body { overflow-x: clip })
 * still prevents the page itself from horizontally scrolling. */
.table-scroll {
  width: 100%;
  overflow-x: hidden;
  overflow-y: visible;
}
/* Custom scrollbar rules apply to any variant of .table-scroll that
 * exposes a horizontal scrollbar (v1.2 opts in via a media-scoped
 * override; v1.1 stays hidden). Height 8px is small enough to sit
 * unobtrusively between the table body and the pagination footer. */
.table-scroll::-webkit-scrollbar { height: 8px; }
.table-scroll::-webkit-scrollbar-track { background: transparent; }
.table-scroll::-webkit-scrollbar-thumb {
  background: var(--gray-20);
  border-radius: 4px;
}
.table-scroll::-webkit-scrollbar-thumb:hover { background: var(--gray-30); }

/* ============================================================================
 * RCM responsive layer (desktop-first, 1440 -> 1280 -> 1024 -> 768)
 * ----------------------------------------------------------------------------
 * The Rate Card Manager has a non-negotiable Action column - users must
 * always be able to click the row's icons. The table's natural width
 * (1304px) exceeds every breakpoint except 1440, so at <1440 the table
 * scrolls horizontally inside .table-scroll and the rightmost column
 * (Actions) sticks to the right edge of that scroll viewport. The page
 * itself never gets a horizontal scrollbar (the `html, body { overflow-x:
 * hidden }` guard above).
 *
 * Sticky behavior is implemented at the cell level on both the header
 * (`.th--action`) and every body row's `.actions` div. Both must be
 * sticky so the column visually stays aligned across header + body
 * when the user scrolls the table horizontally.
 * ========================================================================= */

/* Sticky Action column: works because .table-scroll is the scrolling
 * ancestor (`overflow-x: auto`). `right: 0` pins the cell to the right
 * edge of that scroll container. White background so scrolled cells
 * passing underneath don't bleed through. */
.table__head .th--action,
.row .actions {
  position: sticky;
  right: 0;
  background: var(--surface);
  /* Lift above the regular grid cells + resizers so they don't peek
   * through during scroll (see z-index tier comment on .th-resizer). */
  z-index: 3;
}
/* The header cell also needs to be the full row height of the header
 * (32px) so the white bg fully covers the head border. */
.table__head .th--action {
  height: 100%;
  display: inline-flex;
  align-items: center;
  /* Mirror .actions padding so the "Action" label sits directly
   * above the icon group's left edge. justify-content:flex-start
   * matches the other column headers (Status, SalesHub ID, Rate
   * Card ID, etc.) so the table grid reads consistently. */
  padding: 0 8px;
  justify-content: flex-start;
}

/* Subtle left-edge shadow on the sticky Action column - only shown
 * when the table-scroll is actually horizontally scrolled (toggled by
 * JS via the .is-scrolled class). When there's no scroll the table
 * sits flush so the shadow would be visual noise. */
.table-scroll.is-scrolled .table__head .th--action,
.table-scroll.is-scrolled .row .actions {
  box-shadow: -8px 0 8px -6px rgba(15, 18, 20, 0.12);
}

/* --- Name column (flex-and-shrink, 2026-07-08 overflow brief) --------
 *
 * The Name column is the sole flexible grid track (minmax(0, 1fr))
 * so long rate card names can truncate with ellipsis instead of
 * forcing horizontal overflow. Two rules matter for this to work:
 *
 * 1. min-width: 0 on both the header and body Name cells so they
 *    can shrink below their intrinsic (min-content) width. Without
 *    this, the .th--sortable's `display: inline-flex` + 8px padding
 *    would refuse to compress below ~16px and the "Name" label
 *    would visually overlap the Marketplace column at narrow
 *    viewports. min-width:0 is the standard CSS Grid escape hatch
 *    for shrinkable content tracks.
 *
 * 2. overflow: hidden on the cells so the Name label + link text
 *    are clipped to the track width. The .name__link already
 *    handles ellipsis at the link level, but we belt-and-suspenders
 *    the cell so any inline decoration (sort icon on the header)
 *    is also clipped to the track when the track collapses.
 *
 * The previous sticky-Name-column pattern (left:0 pin during
 * horizontal scroll) has been retired because .table-scroll is now
 * overflow-x:hidden and horizontal scrolling doesn't happen. The
 * .is-name-stuck class continues to be toggled by
 * initStickyNameShadow (kept for consumers who still enable the
 * scroller) but no longer changes layout - only the optional
 * right-edge shadow. */
.table__head .th--name,
.row .name {
  min-width: 0;
  overflow: hidden;
}
/* Header cell fills the 32px head height so the underline divider
 * still lines up with the other headers. */
.table__head .th--name {
  height: 100%;
}

/* Legacy sticky-shadow hook (kept behind .is-name-stuck so it's
 * inert by default). If a consumer re-enables horizontal scroll on
 * .table-scroll, this rule + initStickyNameShadow will re-apply
 * the right-edge shadow without further code changes. */
.table-scroll.is-name-stuck .table__head .th--name,
.table-scroll.is-name-stuck .row .name {
  box-shadow: 8px 0 8px -6px rgba(15, 18, 20, 0.12);
}

/* --- Responsive page padding ------------------------------------------------
 * Desktop-first: 1440+ uses the Figma 68px gutter. Below that we trim
 * the side padding so the table card uses as much horizontal real
 * estate as possible (which in turn delays when the table needs to
 * horizontally scroll). The header buttons stay right-anchored at every
 * breakpoint because the .page__header rule keeps `justify-content:
 * space-between` until it has to wrap.
 *
 *   >=1440: padding-left/right = 68px (.page__top above, .page__content 64)
 *   1280-1439: page__content x-padding 40px (table-card breathing room)
 *   1024-1279: page__content x-padding 24px
 *    <1024  : page__content x-padding 16px (max table width)
 *
 * The .page__content rule sits at line ~395 and uses 64px hardcoded.
 * The override happens here so the desktop value stays the default. */
/* Gutter only ever needs to STAY at 40 (the same value the >=1440
 * default uses), so the .page__content rule above is the single
 * source of truth at desktop widths. The narrower-than-1280 cases
 * still tighten further so very small viewports keep some breathing
 * room without crushing the card. */
@media (max-width: 1279px) {
  .page__content { padding-left: 24px; padding-right: 24px; }
  /* Reduce gap between header buttons before any wrapping happens. */
  .page__actions { gap: 8px; }
}
@media (max-width: 1023px) {
  .page__content { padding-left: 16px; padding-right: 16px; }
  /* The header stays single-row even at 1024 - the actions group keeps
   * its full labels but tightens to 8px button gap (set above). The
   * existing <900 wrap rule still fires below 900 if the buttons
   * truly cannot fit. */
}

/* --- Header buttons: keep labels visible down to 1024 ----------------------
 * The Download/Upload/Create trio reads ~480px wide at desktop. At
 * 1024 viewport the title + buttons together (~480 + ~360 + 16 gap =
 * ~860) still fits inside the 992px content area, so no wrapping
 * needed. The existing <900 wrap rule kicks in below that. Just
 * compress the gap so they breathe on the tighter widths. */
@media (max-width: 1279px) and (min-width: 900px) {
  .page__actions .btn { padding-left: 16px; padding-right: 16px; }
}

/* --- Toolbar at narrow widths: keep search visible but cap shrink ---------
 * The search input shrinks with the toolbar via flex, but if the card
 * gets very narrow we don't want it to collapse below 200px (still
 * usable for typing). The Filter button keeps its 72px natural width. */
@media (max-width: 1279px) {
  .toolbar .search input { min-width: 200px; }
}

/* --- Page header: wrap below 900px -----------------------------------------
 * Default Figma layout is title-left + actions-right at the same height.
 * When width gets tight, allow the action group to flow onto its own row
 * so neither title nor buttons get cropped. Actions stay LEFT-aligned in
 * the wrapped state — visually clear and consistent with the heading. */
@media (max-width: 899px) {
  .page__header {
    flex-wrap: wrap;
    height: auto;          /* Was 65px fixed — let it grow when wrapped */
    padding-bottom: 28px;  /* Slightly more breathing room when stacked */
  }
  .page__heading {
    flex: 1 1 100%;        /* Take full width so actions drop below */
  }
  .page__actions {
    margin-top: 12px;      /* Tighter than the desktop 16px when wrapped */
    align-self: flex-start;
  }
  body [data-page="list"] > .page__top > .page__header {
    justify-content: flex-end;
  }
  body [data-page="list"] .page__actions {
    margin-left: 0;
  }
}

/* --- Toolbar: stack below 700px --------------------------------------------
 * At desktop the toolbar is a horizontal Filter + Search row. Below 700px
 * the search input would either crush or push Filter off-card, so we
 * stack them vertically. Search expands to full card width; Filter stays
 * at its natural 72px on the left. Hit targets remain 32px tall. */
@media (max-width: 699px) {
  .toolbar {
    flex-wrap: wrap;
    height: auto;
    align-items: stretch;
    gap: 8px;
    padding: 12px 16px;
  }
  .toolbar .filter-btn {
    align-self: flex-start;  /* keep button at natural width */
  }
  .toolbar .search {
    width: 100%;             /* fill the row when stacked */
  }
}

/* --- Pagination: wrap below 700px ------------------------------------------
 * Desktop layout is a 3-column grid (left count / center pages / right
 * jumper). At narrow widths these need room to wrap onto multiple lines
 * without overlapping. Switch the footer to flex+wrap so each cluster
 * can move down independently and stays fully clickable. */
@media (max-width: 699px) {
  .footer {
    display: flex;
    flex-wrap: wrap;
    height: auto;
    min-height: 48px;
    align-items: center;
    justify-content: space-between;
    row-gap: 10px;
    column-gap: 16px;
    padding: 12px 16px;
  }
  .footer__left,
  .footer__pager,
  .footer__right {
    justify-self: auto;
  }
  .footer__pager {
    order: 3;
    flex-basis: 100%;
    justify-content: center;
  }
}

/* --- Filter side panel: full-viewport at <600px ----------------------------
 * Desktop spec is a 360px-wide left-anchored panel. On phones / narrow
 * windows the panel needs to cap at viewport width so the footer's Apply
 * / Cancel buttons can never sit off-screen. Footer remains sticky at the
 * bottom of the panel; body scrolls if fields exceed the viewport. */
@media (max-width: 600px) {
  .fpanel {
    width: calc(
      100% - var(--app-safe-area-left) - var(--app-safe-area-right)
    );
    max-width: calc(
      100% - var(--app-safe-area-left) - var(--app-safe-area-right)
    );
  }
}

/* --- Hit-target safety net --------------------------------------------------
 * Touch + zoom users still need >= 32px tap targets at every viewport. */
@media (max-width: 699px) {
  .btn, .filter-btn, .ffield__input,
  .footer select, .footer__pager .page-btn {
    min-height: 32px;
  }
}

/* --- Global nav: progressive collapse ----------------------------------------
 *
 * The desktop nav (1440-1100px) shows: logo + divider + Sales + Planning +
 * Ad Ops + Billing + Admin + bell + avatar. Below ~1100px the items start
 * pushing the right-side cluster off-screen (avatar gets clipped, then bell,
 * then Admin). Profile menu reachability is non-negotiable, so we hide section
 * items in priority order (least-important first) and ALWAYS preserve:
 *
 *   - Disney logo + divider (brand)
 *   - Admin (the active route — the user is currently on Admin → Rate Card)
 *   - Bell + Avatar (notifications + profile menu)
 *
 * Items are hidden via `display: none` on the <li>, never `visibility:hidden`,
 * so layout reclaims the space. The hidden routes remain reachable through
 * Admin's own routing (this is a single-screen demo; in production a
 * hamburger would replace them, but per spec we don't redesign).
 *
 *   ≤1100px : hide Billing
 *   ≤ 980px : hide Billing + Ad Ops
 *   ≤ 860px : hide Billing + Ad Ops + Planning
 *   ≤ 740px : hide Billing + Ad Ops + Planning + Sales (only Admin left)
 *   ≤ 480px : also hide Admin (logo + bell + avatar only)
 */
@media (max-width: 1100px) {
  .gnav__item[data-route="billing"] { display: none; }
}
@media (max-width: 980px) {
  .gnav__item[data-route="ad-ops"] { display: none; }
}
@media (max-width: 860px) {
  .gnav__item[data-route="planning"] { display: none; }
}
@media (max-width: 740px) {
  .gnav__item[data-route="sales"] { display: none; }
}
@media (max-width: 480px) {
  .gnav__item[data-route="admin"] { display: none; }
  /* With no nav items at all, tighten the left module so logo+divider don't
   * float in the middle of the bar. */
  .gnav__module { padding-left: 16px; }
  .gnav__divider { display: none; }
}
/* Whatever the width, never let the right-side cluster get clipped — the
 * action buttons must stay reachable. */
@media (max-width: 1100px) {
  .gnav { padding-right: 12px; }
  .gnav__right { flex-shrink: 0; }
  .gnav__items { flex-shrink: 1; min-width: 0; overflow: hidden; }
}

/* --- Page-content padding: prevent the table card from being chopped --------
 * The table card surface sits inside .page__content which uses the same
 * page gutter as the header. At <600px the desktop 68px gutter is too
 * generous, plus the footer pagination row grows to 99px (wrap) or 141px
 * (full wrap) — we need page-bottom padding so the wrapped footer doesn't
 * land flush against the viewport edge. */
@media (max-width: 699px) {
  .page__content {
    padding-bottom: 24px;
  }
}
@media (max-width: 480px) {
  .page__content {
    padding-bottom: 32px;
  }
}

/* --- Dropdown menus: flip anchor at narrow widths --------------------------
 *
 * Issue: at desktop the Import CSV dropdown is right-aligned to its trigger
 * (`right: 0`). The trigger sits on the far right of the page header so the
 * menu opens leftward into available space. When the page header wraps below
 * the title at ≤899px, the trigger moves to the LEFT of the page (x≈24).
 * The 260-280px menu then overflows OUT THE LEFT side of the viewport.
 *
 * Fix: when the page header is wrapped, flip the trigger's dropdown to
 * left-anchored so the menu opens rightward into available space instead.
 * We scope by the parent .page__actions context so we don't touch the
 * profile menu (which still belongs on the right of the avatar). */
@media (max-width: 899px) {
  .page__actions .dropdown__menu {
    right: auto;
    left: 0;
  }
}

/* --- Theme submenu: flip below parent at very narrow widths ----------------
 *
 * Desktop layout puts the Theme submenu to the LEFT of the profile menu
 * (right: calc(100% + 8px)). At ≤480px the profile menu itself is already
 * close to the right edge of the viewport (~280px wide menu in a 390/480
 * viewport), so the submenu's 184px width gets pushed past the left edge.
 *
 * Fix: at ≤480px, render the submenu BELOW the Theme menu item instead of
 * to its left. Width stays 184px, alignment matches the menu item's left.
 * The submenu list itself is short (2 items) so the extra vertical space
 * is fine even on a 500px-tall viewport.
 *
 * The .profile-menu width is 260px and the .theme-submenu is 184px so this
 * always fits at any viewport ≥184px (well under our 390px floor).
 */
@media (max-width: 480px) {
  .theme-submenu {
    right: auto;
    left: 0;
    top: calc(100% + 4px);
  }
}

/* ============================================================================
 * ADS PREVIEW THEME LAYER
 * ============================================================================
 *
 * Architecture rules (per spec):
 *   1. EDL Light is the DEFAULT. Nothing above this block is gated on a
 *      theme class — body.theme-edl is purely informational and the existing
 *      rules apply to it through normal cascade.
 *   2. Every ADS rule below is prefixed with `.theme-ads`. Without the body
 *      class no ADS rule can affect the page.
 *   3. We override the EXISTING markup; no DOM changes, no JS-layout changes.
 *      The product (filter logic, pagination logic, dataset, row renderer)
 *      is shared 1:1 with EDL — only visual surface tokens differ.
 *
 * Tokens below are the AUTHORITATIVE values from the Ads Design System
 * Figma file (DJb3yM8aOQjIAbTQ9Cd8Mi), pulled via Figma MCP
 * get_variable_defs against the real component nodes — not guessed:
 *
 *   Button     17:2 / 17:137 / 17:272 / 17:407
 *   Search     37:2
 *   Nav top    795:40        TopBarTab  794:40 / 794:42
 *   Dropdown   36:12
 *   Select     2551:31
 *   Popover    2693:20
 *   Card       1995:18
 *   Table H/B  1727:13113 / 1727:13167 / 1727:13174 / 1727:13181
 *   Chip       52:194 (Success) / 52:290 (Warning) / 52:386 (Error)
 *   Pagination 2140:46 / 2140:52
 *   Modal      38:2          Sheet      598:1380
 *   Color page 1796:2689     Typography page 20:28
 *
 *  Color palette                Type / role
 *    indigo/300   #4045C2       Brand. Primary button bg, link, active
 *                               pagination, selected-option check.
 *    indigo/50    #E2E3F2       surface/brand-wash (selected row,
 *                               disabled primary).
 *    indigo/300-hover #6567CE   Primary button hover.
 *    gray/600     #1E2528       text/primary.
 *    gray/500ish  #3B4347       text/secondary.
 *    gray/60      #51585B       text/tertiary, icons, table header.
 *    black/op/20  rgba(15,18,20,0.20)  border/default.
 *    black/op/10  rgba(15,18,20,0.10)  table divider.
 *    black/op/05  rgba(15,18,20,0.05)  surface/secondary (table head bg),
 *                                       hover bg.
 *    white        #FFFFFF       surface/default.
 *    green/10..70 #E0EDE5/#365944  Success chip bg / text.
 *    orange/10..70 #FAE2C8/#5C3711 Warning chip bg / text.
 *    red/10..70   #F8E0E2/#993B44  Error chip bg / text.
 *
 *  Radii   button/popover/dropdown/select/card 6 · search/chip 100 · modal 12
 *  Type    Open Sans 14/20 body/md, 13/18 sm, 18/24 xl (titles).
 *  Focus   2px indigo/300 ring (color/border/focus → indigo/300).
 *
 * IMPORTANT: ADS top nav background is `#4045C2` — the SAME indigo as EDL.
 * That's not coincidence; the EDL nav uses Disney's internal brand color,
 * and ADS's `navigation/topbar/background` resolves to the same Indigo 300.
 * Where ADS visibly differs from EDL is in radius (6 vs many), font
 * (Open Sans vs Inspire — Inspire is explicitly NOT in ADS per the
 * Typography page note), chip palette (semantic green/orange vs indigo),
 * row hover, button border weight, header letter-spacing/case, and active
 * pagination treatment (filled brand pill vs underline). */

/* -------- Theme-scoped CSS variables. Only resolved when body.theme-ads. */
body.theme-ads {
  /* ---- ADS design tokens ---- */
  --ads-brand:           #4045C2;          /* indigo/300 */
  --ads-brand-hover:     #6567CE;
  --ads-brand-wash:      #E2E3F2;          /* indigo/50 — selected row, disabled primary */
  --ads-text-primary:    #1E2528;          /* color/text/primary (gray/600) */
  --ads-text-secondary:  #3B4347;          /* color/text/secondary */
  --ads-text-tertiary:   #51585B;          /* color/text/tertiary, gray/60 */
  /* Placeholder text uses 0F1214 @ 30% across all field families
   * (input, select, date picker, search, textarea). This overrides
   * the ADS Figma "rest placeholder" #51585B which the team has
   * decided reads too dark next to the surrounding labels. The same
   * 30% black is also used for the ADS disabled-state text, which
   * keeps disabled + placeholder visually unified. */
  --ads-text-placeholder: rgba(15, 18, 20, 0.30);
  --ads-text-disabled:   rgba(15, 18, 20, 0.30);
  --ads-text-on-primary: #FFFFFF;
  --ads-border-default:  rgba(15, 18, 20, 0.20);  /* black/opacity/20 */
  --ads-border-bold:     rgba(15, 18, 20, 0.50);  /* tertiary button border */
  --ads-border-recessed: rgba(15, 18, 20, 0.10);  /* table row divider, modal divider */
  --ads-border-subtle:   rgba(15, 18, 20, 0.05);  /* table head border, hover bg */
  --ads-surface:         #FFFFFF;
  --ads-surface-hover:   rgba(15, 18, 20, 0.05);  /* tertiary/ghost/secondary hover */
  --ads-surface-active:  rgba(15, 18, 20, 0.10);
  --ads-surface-table-head: rgba(15, 18, 20, 0.05); /* surface/secondary */
  --ads-scrim:           rgba(0, 0, 0, 0.50);
  --ads-shadow-popover:  0 4px 12px rgba(15, 18, 20, 0.30);  /* elevation/30 */
  --ads-shadow-sheet:    0 4px 16px rgba(15, 18, 20, 0.30);  /* sheet elevation */
  --ads-shadow-card:     0 0 4px rgba(23, 29, 35, 0.13);  /* card low / elevation/10 */
  --ads-radius-sm:       6px;
  --ads-radius-md:       12px;             /* modal */
  --ads-radius-card:     16px;
  --ads-radius-pill:     100px;            /* search, chip */
  --ads-radius-day:      3px;              /* date cell rest */
  --ads-radius-day-active: 6px;            /* date cell today / selected */
  /* ADS Modal, Figma 38:46. Names normalize the official component tokens. */
  --ads-modal-width: 480px;
  --ads-modal-background: var(--ads-surface);
  --ads-modal-divider: var(--ads-border-recessed);
  --ads-modal-title-text: var(--ads-text-primary);
  --ads-modal-body-text: var(--ads-text-secondary);
  --ads-modal-radius: var(--ads-radius-md);
  --ads-modal-padding: 24px;
  --ads-modal-header-padding-y: 20px;
  --ads-modal-body-padding-y: 16px;
  --ads-modal-footer-padding-y: 16px;
  --ads-modal-footer-gap: 12px;
  --ads-modal-close-size: 24px;
  --ads-modal-close-icon-size: 15px;
  --ads-modal-close-radius: 3px;
  /* ADS Pagination 35:36. */
  --ads-pagination-item-size: 36px;
  --ads-pagination-item-padding: 8px;
  --ads-pagination-item-radius: 8px;
  --ads-pagination-gap: 4px;

  /* ADS Checkbox, Figma 71:62. */
  --ads-checkbox-control-size: 20px;
  --ads-checkbox-box-size: 16px;
  --ads-checkbox-box-border-width: 1.5px;
  --ads-checkbox-box-radius: 2px;
  --ads-checkbox-row-padding: 4px;
  --ads-checkbox-content-gap: 8px;
  --ads-checkbox-copy-gap: 4px;
  --ads-checkbox-check-width: 9.375px;
  --ads-checkbox-check-height: 6.75px;
  --ads-checkbox-minus-width: 9px;
  --ads-checkbox-minus-height: 0.75px;
  --ads-checkbox-box-fill: var(--ads-surface);
  --ads-checkbox-box-border: var(--ads-border-default);
  --ads-checkbox-box-fill-checked: var(--ads-brand);
  --ads-checkbox-box-fill-disabled: var(--ads-border-subtle);
  --ads-checkbox-box-invalid: var(--ads-danger-border);
  /* The check and minus ride on the checked box fill, so they take the
   * on-primary token. Disabled swaps to text/disabled because the
   * disabled box fill is a 5% wash that white would vanish into. */
  --ads-checkbox-glyph: var(--ads-text-on-primary);
  --ads-checkbox-glyph-disabled: var(--ads-text-disabled);

  /* ---- ADS Danger / Status ----------------------------------------
   * Single source of truth for destructive-button / error-state colors.
   * Pulled from the Color page red ramp (red/30 .. red/700):
   *   --ads-danger-bg     red/600 fill (in-page Remove buttons)
   *   --ads-danger-hover  red/700 fill on hover
   *   --ads-danger-border red/30 (error-state input border)
   *   --ads-danger-text   red/700 (error-message text)
   * Confirmation dialogs deliberately do NOT resolve to these: every
   * modal confirm, destructive or not, uses the ADS Primary Button
   * (7722-229) and carries its consequence in the wording. Red is
   * reserved for in-page affordances and error text. */
  --ads-danger-bg:           #C73945;     /* red/600 - destructive button fill */
  --ads-danger-hover:        #993B44;     /* red/700 - destructive button hover */
  --ads-danger-border:       #DB5461;     /* red/30  - input error border */
  --ads-danger-text:         #993B44;     /* red/700 - error message text */
  --ads-danger-icon:         #C73945;     /* red/600 - destructive icon hover (Delete) */
  /* Positive / success - used for the accordion "Completed" badge and
   * any other quiet success affordance. Same green family the toast
   * success icon uses (#167E3E), darkened a step for accessible text. */
  --ads-positive-text:       #167E3E;     /* green/700 - success text */
  --ads-positive-bg:         #E6F4EB;     /* green/10  - success surface */

  /* ---- ADS Field / Input / Select / Date Picker form-control tokens.
   * Sourced directly from Figma component specs (component IDs noted):
   *   Input              63:216
   *   Select             2551:55
   *   Select Option      2550:30
   *   Date Picker        687:2824
   *   Date Cell          678:2837
   * All three controls share the same trigger height (36) + radius (6) +
   * Open Sans body/normal/md (14/20) typography. Padding differs:
   *   Input        8y / 12x
   *   Select       6y / 12x   (note: 6, not 8)
   *   Date Picker  8y / 12x
   * Borders all 1px rest, 2px active. Error color = #DB5461 (not the
   * older EDL #C8242D). Disabled bg = surface, border = black/opacity/05.
   */
  --ads-input-height:        36px;
  --ads-input-radius:        6px;
  --ads-input-pad-y:         8px;
  --ads-input-pad-x:         12px;
  --ads-input-border-width:  1px;
  --ads-input-border-active: 2px;
  --ads-input-border-rest:   rgba(15, 18, 20, 0.20);    /* border/default */
  --ads-input-border-hover:  rgba(15, 18, 20, 0.50);    /* border/bold */
  --ads-input-border-active-color: var(--ads-brand);
  --ads-input-border-error:  var(--ads-danger-border, #DB5461); /* status/error/border (red/30) */
  --ads-input-border-disabled: rgba(15, 18, 20, 0.05);  /* surface/disabled */
  /* Per Figma 63:101 (Input, State=Disabled) the DISABLED background
   * uses --input/state/rest/background (white). Only the border + text
   * change to muted treatments. Setting bg to white prevents the
   * "filled gray" disabled look that's not in the ADS spec. */
  --ads-input-bg-disabled:   #FFFFFF;
  --ads-input-text:          var(--ads-text-primary);
  --ads-input-text-disabled: rgba(15, 18, 20, 0.30);
  /* Per the brief: all placeholder text uses 0F1214 @ 30%, not the
   * ADS-Figma rest-state text-tertiary. Single token cascades to
   * .field__input, .edl-select__value--placeholder, and
   * .ads-datepicker__value--placeholder via the rules further down. */
  --ads-input-placeholder:   rgba(15, 18, 20, 0.30);
  --ads-input-error-text:    var(--ads-danger-text, #993B44); /* field-validation/type/error/text */

  /* ADS Search, Figma 37:16. Names normalize the official component tokens. */
  --ads-search-height: 36px;
  --ads-search-gap: 8px;
  --ads-search-padding-x: 12px;
  --ads-search-padding-y: 8px;
  --ads-search-radius: 100px;
  --ads-search-icon-size: 20px;
  --ads-search-icon-leaf-size: 16.2676px;
  --ads-search-clear-size: 16px;
  --ads-search-clear-leaf-size: 10.0006px;
  --ads-search-rest-background: var(--ads-surface);
  --ads-search-disabled-background: var(--ads-surface-hover);
  --ads-search-border-default: var(--ads-border-default);
  --ads-search-border-bold: var(--ads-border-bold);
  --ads-search-border-disabled: var(--ads-input-border-disabled);
  --ads-search-rest-text: var(--ads-text-tertiary);
  --ads-search-filled-text: var(--ads-text-primary);
  --ads-search-disabled-opacity: 0.6;

  --ads-label-color:         #3B4347;       /* field-description/state/default/text */
  --ads-label-size:          14px;          /* floor: no visible text under 14px */
  --ads-label-line:          18px;
  --ads-label-gap:           8px;           /* label-to-text-box gap (Input + Select) */
  --ads-label-gap-datepicker: 4px;          /* label-to-text-box gap (Date Picker) */

  --ads-select-pad-y:        6px;           /* Select trigger uses 6y, not 8y */

  --ads-popover-radius:      6px;
  --ads-popover-border:      rgba(15, 18, 20, 0.20);
  --ads-popover-bg:          #FFFFFF;
  --ads-popover-shadow:      var(--ads-shadow-popover); /* elevation/30 */
  --ads-popover-pad:         16px;          /* Date Picker calendar body padding */

  --ads-option-height:       36px;
  --ads-option-pad-y:        8px;
  --ads-option-pad-x:        12px;
  --ads-option-gap:          8px;
  --ads-option-selected-bg:  #E2E3F2;       /* surface/brand-wash = indigo/50 */
  --ads-option-hover-bg:     rgba(15, 18, 20, 0.05);

  /* ADS Dropdown, Size=Small (Figma 36:56). */
  --ads-dropdown-default-pad-x: 12px;
  --ads-dropdown-default-pad-y: 10px;
  --ads-dropdown-default-font-size: 14px;
  --ads-dropdown-default-line-height: 20px;
  --ads-dropdown-default-caret-box: 20px;
  --ads-dropdown-default-caret-width: 13.75px;
  --ads-dropdown-default-caret-height: 7.5px;
  --ads-dropdown-small-pad-x: 10px;
  --ads-dropdown-small-pad-y: 6px;
  --ads-dropdown-small-font-size: 14px;   /* floor: no visible text under 14px */
  --ads-dropdown-small-caret-box: 16px;
  --ads-dropdown-small-caret-width: 11px;
  --ads-dropdown-small-caret-height: 6px;
  --ads-dropdown-menu-gap: 4px;
  --ads-control-disabled-opacity: 0.6;
  --ads-dropdown-border-rest: var(--ads-border-default);
  --ads-dropdown-border-hover: var(--ads-border-bold);
  --ads-dropdown-border-open: var(--ads-border-default);
  --ads-dropdown-border-disabled: var(--ads-border-subtle);
  --ads-dropdown-menu-surface: var(--ads-surface);
  --ads-dropdown-menu-border: var(--ads-border-default);
  --ads-dropdown-option-state: var(--ads-surface-hover);

  --ads-day-cell-width:      33px;
  --ads-day-cell-height:     28px;

  /* ---- ADS Chip tokens (Status column).
   * Pulled directly from the Figma Chip component (137:3432) via
   * get_design_context against node 137:3839 (Info / Published) and
   * 142:4303 (Default / Draft). These are the canonical chip/style/*
   * token values - do not replace with sampled pixel colors from
   * downstream reference images.
   *   Info variant (Published/Active/Pending Review/Archived)
   *     chip/style/info/background  = #DDE6F7
   *     chip/style/info/border      = #DDE6F7   (same as bg = borderless look)
   *     chip/style/info/text        = #143F90
   *   Default variant (Draft)
   *     chip/style/default/background = #ECEEEE
   *     chip/style/default/border     = rgba(15,18,20,0.10)
   *     chip/style/default/dismiss    = #1E2528   (text token name in Figma) */
  --ads-chip-info-bg:        #DDE6F7;
  --ads-chip-info-border:    #DDE6F7;
  --ads-chip-info-text:      #143F90;
  --ads-chip-default-bg:     #ECEEEE;
  --ads-chip-default-border: rgba(15, 18, 20, 0.10);
  --ads-chip-default-text:   #1E2528;
  /* ADS Table Header Cell, Figma 7737:210 / 105:9. */
  --ads-table-header-text:       var(--ads-text-tertiary);
  --ads-table-header-font-size:  13px;
  --ads-table-header-line-height: 18px;
  --ads-table-header-font-weight: 400;
  --ads-table-sort-icon-size: 16px;
  --ads-table-header-gap:        6px;
  --ads-table-header-padding-x:  12px;
  --ads-table-header-padding-y:  10px;

  /* Compatibility aliases prevent legacy base rules from leaking EDL
   * colors or typography into the active ADS theme. */
  --indigo-70: var(--ads-brand);
  --indigo-30: var(--ads-brand-hover);
  --indigo-15: var(--ads-brand-wash);
  --indigo-10: var(--ads-brand-wash);
  --indigo-5: var(--ads-brand-wash);
  --gray-95: var(--ads-text-primary);
  --gray-70: var(--ads-text-tertiary);
  --gray-50: var(--ads-text-secondary);
  --gray-30: var(--ads-border-default);
  --gray-20: var(--ads-border-recessed);
  --surface: var(--ads-surface);
  --nav-bg: var(--ads-brand);
  --nav-text: var(--ads-text-on-primary);
  --placeholder-color: var(--ads-text-placeholder);
  --table-toolbar-text: 14px/20px "Open Sans", sans-serif;
  --table-header-text: 12px/18px "Open Sans", sans-serif;
  --table-body-text: 14px/20px "Open Sans", sans-serif;
  --table-link-text: 14px/20px "Open Sans", sans-serif;
  --table-chip-text: 12px/18px "Open Sans", sans-serif;
  --table-pagination-text: 14px/20px "Open Sans", sans-serif;
  --table-control-text: 14px/20px "Open Sans", sans-serif;

  /* ---- Page-level swap ----
   * Body bg = Gray-05 so the page canvas matches the .theme-ads
   * .page__content band. If body stayed white (--ads-surface), tall
   * viewports show a white block below the table card where the body
   * background bleeds past the gray content band. */
  font-family: "Open Sans", -apple-system, BlinkMacSystemFont,
    "Segoe UI", Helvetica, Arial, sans-serif;
  font-feature-settings: normal;
  background: var(--gray-05);
  color: var(--ads-text-primary);
}

/* ---------------------------------------------------------------------------
 * ADS CHECKBOX, component set 71:62.
 * ------------------------------------------------------------------------ */
.theme-ads .ads-checkbox {
  position: relative;
  display: inline-flex;
  align-items: flex-start;
  gap: var(--ads-checkbox-content-gap);
  padding: var(--ads-checkbox-row-padding);
  color: var(--ads-text-primary);
  font-family: "Open Sans", sans-serif;
  cursor: pointer;
}

.theme-ads .ads-checkbox__input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}

.theme-ads .ads-checkbox__control {
  display: grid;
  flex: 0 0 var(--ads-checkbox-control-size);
  place-items: center;
  width: var(--ads-checkbox-control-size);
  height: var(--ads-checkbox-control-size);
}

.theme-ads .ads-checkbox__box {
  position: relative;
  display: grid;
  place-items: center;
  width: var(--ads-checkbox-box-size);
  height: var(--ads-checkbox-box-size);
  overflow: hidden;
  background: var(--ads-checkbox-box-fill);
  border: var(--ads-checkbox-box-border-width) solid var(--ads-checkbox-box-border);
  border-radius: var(--ads-checkbox-box-radius);
}

.theme-ads .ads-checkbox__check,
.theme-ads .ads-checkbox__minus {
  display: none;
  color: var(--ads-checkbox-glyph);
}

.theme-ads .ads-checkbox__check {
  width: var(--ads-checkbox-check-width);
  height: var(--ads-checkbox-check-height);
}

.theme-ads .ads-checkbox__minus {
  width: var(--ads-checkbox-minus-width);
  height: var(--ads-checkbox-minus-height);
}

.theme-ads .ads-checkbox__content {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: var(--ads-checkbox-copy-gap);
}

.theme-ads .ads-checkbox__label {
  color: var(--ads-text-primary);
  font-size: 14px;
  font-weight: 600;
  line-height: 20px;
}

.theme-ads .ads-checkbox__description {
  color: var(--ads-text-secondary);
  font-size: 14px;
  font-weight: 400;
  line-height: 18px;
}

.theme-ads .ads-checkbox:hover .ads-checkbox__box {
  border-color: var(--ads-border-bold);
}

.theme-ads .ads-checkbox__input:focus-visible + .ads-checkbox__control .ads-checkbox__box {
  outline: var(--ads-input-border-active) solid var(--ads-brand);
  outline-offset: var(--ads-input-border-width);
}

.theme-ads .ads-checkbox__input:checked + .ads-checkbox__control .ads-checkbox__box,
.theme-ads .ads-checkbox__input:indeterminate + .ads-checkbox__control .ads-checkbox__box {
  background: var(--ads-checkbox-box-fill-checked);
  border-color: var(--ads-checkbox-box-fill-checked);
}

.theme-ads .ads-checkbox__input:checked + .ads-checkbox__control .ads-checkbox__check,
.theme-ads .ads-checkbox__input:indeterminate + .ads-checkbox__control .ads-checkbox__minus {
  display: block;
}

.theme-ads .ads-checkbox.is-invalid .ads-checkbox__box,
.theme-ads .ads-checkbox__input[aria-invalid="true"] + .ads-checkbox__control .ads-checkbox__box {
  border-color: var(--ads-checkbox-box-invalid);
}

.theme-ads .ads-checkbox.is-invalid .ads-checkbox__label,
.theme-ads .ads-checkbox.is-invalid .ads-checkbox__description {
  color: var(--ads-danger-text);
}

.theme-ads .ads-checkbox__input:checked[aria-invalid="true"] + .ads-checkbox__control .ads-checkbox__box,
.theme-ads .ads-checkbox__input:indeterminate[aria-invalid="true"] + .ads-checkbox__control .ads-checkbox__box,
.theme-ads .ads-checkbox.is-invalid .ads-checkbox__input:checked + .ads-checkbox__control .ads-checkbox__box,
.theme-ads .ads-checkbox.is-invalid .ads-checkbox__input:indeterminate + .ads-checkbox__control .ads-checkbox__box {
  background: var(--ads-checkbox-box-invalid);
}

.theme-ads .ads-checkbox:has(.ads-checkbox__input:disabled) {
  cursor: default;
}

.theme-ads .ads-checkbox__input:disabled + .ads-checkbox__control .ads-checkbox__box {
  background: var(--ads-checkbox-box-fill-disabled);
  border-color: var(--ads-checkbox-box-fill-disabled);
}

.theme-ads .ads-checkbox__input:disabled + .ads-checkbox__control .ads-checkbox__check,
.theme-ads .ads-checkbox__input:disabled + .ads-checkbox__control .ads-checkbox__minus {
  color: var(--ads-checkbox-glyph-disabled);
}

.theme-ads .ads-checkbox__input:disabled ~ .ads-checkbox__content .ads-checkbox__label,
.theme-ads .ads-checkbox__input:disabled ~ .ads-checkbox__content .ads-checkbox__description {
  color: var(--ads-text-disabled);
}

/* ---------------------------------------------------------------------------
 *  NAVIGATION  (component 20:16 — Navigation, TopBarTab 794:40/794:42,
 *  Navigation Top Bar 795:40)
 *  Background = indigo/300 (#4045C2), text = white, padding-x 24, tab gap 4,
 *  tab padding 8/12. Active state is brand-wash on tab.
 * ------------------------------------------------------------------------ */
/* ADS theme nav: same as EDL now per Figma 184:4469. Background is
 * navigation/topbar/background (#4045C2 = ads-brand) and ALL text +
 * icons render in pure white. The earlier Indigo/10 (#E6E6FF) inactive
 * treatment is replaced by white per the new spec. */
.theme-ads .gnav {
  background: var(--ads-brand);
  font-family: "Open Sans", sans-serif;
}
.theme-ads .gnav__action,
.theme-ads .gnav__logomark,
.theme-ads .gnav__item,
.theme-ads .gnav__brand-name {
  color: #FFFFFF;
}
.theme-ads .gnav__action--bell,
.theme-ads .gnav__action--bell:hover,
.theme-ads .gnav__action--apps,
.theme-ads .gnav__action--apps:hover { color: #FFFFFF; }
.theme-ads .gnav__divider {
  background: rgba(255, 255, 255, 0.20);
}

/* ---------------------------------------------------------------------------
 *  PAGE LAYOUT
 *  Page surface stays white (surface/default). No gray band — ADS uses a
 *  single white canvas with cards providing the surface separation.
 * ------------------------------------------------------------------------ */
.theme-ads .page__top {
  background: var(--ads-surface);
  border-bottom: 1px solid var(--ads-border-recessed);
}
/* Page band behind the table card is Gray-05 (#F7F9FA) per Figma 129-5535.
 * Do NOT use --ads-surface here, which is white and would erase the
 * visible gray gutter around the white table card. */
.theme-ads .page__content {
  background: var(--gray-05);
}
/* ADS subtitle per Figma 129:5530: body/normal/md, color #171C20 (Gray/95).
 * (Previously rendered with --ads-text-secondary which is Gray/70 #3B4347.) */
.theme-ads .page__subtitle {
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  line-height: 20px;
  color: #171C20;
}

/* ---------------------------------------------------------------------------
 *  BUTTONS  (component 17:542)
 *  Primary   indigo/300 fill, white text, no border, radius 6
 *  Secondary white fill, brand text, 1px brand border, radius 6
 *  Tertiary  transparent, gray/60 text, 1px black/op/50 border
 *  Ghost     transparent, gray/60 text, no border
 *  All sizes: gap 4, default padding 8/12.
 *  Mapping to existing classes:
 *    .btn--primary  → ADS Primary  (Create Rate Card, Apply)
 *    .btn--secondary → ADS Secondary (Import CSV, Cancel)
 *    .btn--danger   → red/600 fill, white text (per Red scale on Color page)
 * ------------------------------------------------------------------------ */
.theme-ads .btn {
  font-family: "Open Sans", sans-serif;
  font-weight: 600;
  font-size: 14px;
  line-height: 20px;
  border-radius: var(--ads-radius-sm);
  gap: 4px;
}
.theme-ads .btn--primary,
.theme-ads .page__actions .btn--primary {
  background: var(--ads-brand);
  color: var(--ads-text-on-primary);
  border: 1px solid var(--ads-brand);
}
.theme-ads .btn--primary:hover {
  background: var(--ads-brand-hover);
  border-color: var(--ads-brand-hover);
}
.theme-ads .btn--primary:active {
  background: var(--ads-brand);
  border-color: var(--ads-brand);
}
.theme-ads .btn--primary:focus-visible {
  outline: 2px solid var(--ads-brand);
  outline-offset: 2px;
}

/* ---- ADS Button: disabled state ------------------------------------
 * ADS Button (Figma 17:542) declares a "disabled" state across every
 * variant (see ads-components.json - Button.states). Per the brief's
 * Edit-mode dirty tracking, Save Changes must render in this state
 * until the user actually changes a field. Visual treatment matches
 * the ADS disabled spec:
 *   - Lower fill / text opacity to read as muted
 *   - cursor: not-allowed so it's clear the button isn't clickable
 *   - No hover/active state changes
 *   - Same outer geometry as enabled (height/padding/radius) so
 *     toggling disabled <-> enabled never shifts layout
 *
 * The [disabled] attribute alone already prevents pointer events on
 * <button>, but we belt-and-suspenders by also pointer-events: none
 * on the styled rule so any nested <span>/icon clicks also no-op. */
.btn[disabled],
.btn:disabled {
  cursor: not-allowed;
  pointer-events: none;
  /* Keep geometry identical to the enabled state - only color/opacity
   * differ - so toggling Save Changes never reflows surrounding
   * buttons in the create__actions row. */
}
.theme-ads .btn--primary[disabled],
.theme-ads .btn--primary:disabled {
  background: var(--ads-brand);
  border-color: var(--ads-brand);
  color: var(--ads-text-on-primary);
  opacity: 0.4;
}
.theme-ads .btn--secondary[disabled],
.theme-ads .btn--secondary:disabled,
.theme-ads .btn--tertiary[disabled],
.theme-ads .btn--tertiary:disabled,
.theme-ads .btn--ghost[disabled],
.theme-ads .btn--ghost:disabled {
  opacity: 0.4;
}
.theme-ads .btn--secondary,
.theme-ads .page__actions .btn--secondary {
  background: transparent;
  color: var(--ads-brand);
  border: 1px solid var(--ads-brand);
}
.theme-ads .btn--secondary:hover {
  background: var(--ads-surface-hover);    /* button/variant/secondary/hover */
}
.theme-ads .btn--secondary:active,
.theme-ads .btn--tertiary:active,
.theme-ads .btn--ghost:active {
  background: var(--ads-surface-active);
}
/* Ghost button — ADS ghost variant (button/variant/ghost/text) */
.theme-ads .btn--ghost {
  background: transparent;
  color: var(--ads-text-tertiary);         /* ADS ghost text = gray/60 */
  border: 0;
}
.theme-ads .btn--ghost:hover {
  background: var(--ads-surface-hover);
  color: var(--ads-text-primary);
  text-decoration: none;
}
.theme-ads .btn--danger {
  background: var(--ads-danger-bg);
  color: var(--ads-text-on-primary);
  border: 1px solid var(--ads-danger-bg);
}
.theme-ads .btn--danger:hover {
  background: var(--ads-danger-hover);
  border-color: var(--ads-danger-hover);
}
.theme-ads .btn--danger:active {
  background: var(--ads-danger-bg);
  border-color: var(--ads-danger-bg);
}
.theme-ads .btn--danger:focus-visible {
  outline: 2px solid var(--ads-danger-bg);
  outline-offset: 2px;
}
.theme-ads .btn--danger[disabled],
.theme-ads .btn--danger:disabled {
  background: var(--ads-danger-bg);
  border-color: var(--ads-danger-bg);
  color: var(--ads-text-on-primary);
  opacity: 0.4;
}

/* ---------------------------------------------------------------------------
 *  RATE CARD LIST: CARD HEADER
 *
 *  Figma 472:17713 (node 719:7257) puts the title, description and the
 *  three page actions inside the same white card as the toolbar and the
 *  table, rather than in a band floating above it. The header is the
 *  card's first row, so it carries only the horizontal padding the
 *  toolbar and the table use; the card's own 16px top padding supplies
 *  the space above it.
 * ------------------------------------------------------------------------ */
/* 608:13845 py-16. The header is the card's first row and the pagination
   footer its last, so the card itself holds them off its edges. */
[data-page="list"] .page__content > .surface {
  padding-block: 16px;
}

[data-page="list"] .surface__inner > .page__header {
  align-items: center;                 /* 719:7257 items-center */
  justify-content: space-between;      /* 719:7257 justify-between */
  padding: 0 16px;                     /* 719:7257 px-16, matching .toolbar */
  max-width: none;
  margin: 0;
  /* One row at the desktop widths Figma draws. Narrower than that the
   * three actions cannot sit beside the title without running over it,
   * so they drop to their own line instead. */
  flex-wrap: wrap;
  row-gap: 12px;
}

/* 719:7258 stacks the title and the description 8px apart. */
[data-page="list"] .surface__inner > .page__header .page__heading {
  display: flex;
  flex: 1 1 320px;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

/* The description already sits in the gap above, so it adds none of its
   own. Both subtitle siblings are styled: CSS picks which one shows. */
[data-page="list"] .surface__inner > .page__header .page__subtitle {
  margin: 0;
}

/* 719:7265 keeps the three actions on one line, 12px apart, top aligned
   with each other so they read as one group next to the title. */
[data-page="list"] .surface__inner > .page__header .page__actions {
  flex: 0 1 auto;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  width: auto;
}

/* ---------------------------------------------------------------------------
 *  TABLE CARD  (Card component 1995:18, Elevation/10)
 *
 *  Per the 2026-06-25 brief the Rate Card Manager table now matches
 *  the elevation treatment used by the Create page's CARD details
 *  section. Tokens (also applied in the base .surface rule for the
 *  EDL theme):
 *
 *    background:     #FFFFFF (--ads-surface in ADS theme)
 *    border-radius:  16px
 *    box-shadow:     0 0 4px 0 rgba(23, 29, 35, 0.13)
 *
 *  No border. The Gray-05 page band still provides the underlying
 *  canvas; the soft shadow + 16px radius lifts the card visually
 *  without feeling modal-heavy.
 * ------------------------------------------------------------------------ */
.theme-ads .surface {
  background: var(--ads-surface);
  border: 0;
  border-radius: var(--ads-radius-card);
  box-shadow: var(--ads-shadow-card);
}

/* ---------------------------------------------------------------------------
 *  TOOLBAR — FILTER BUTTON + SEARCH
 *  Filter pill mirrors a Secondary Button (radius 6, brand outline).
 *  Search uses the Search component (radius 100 pill — the ADS rule, the
 *  only pill-shaped control besides Chip).
 * ------------------------------------------------------------------------ */
/* ADS theme Filter button per Figma 129-5515 visual reference:
 * Indigo brand outline + indigo text + funnel icon (ADS Button secondary
 * variant with indigo brand color, NOT the gray tertiary variant). This
 * matches the Figma screenshot rendering even though the code-connect
 * snippet was generic Button. Active state stays brand-wash for emphasis. */
.theme-ads .filter-btn {
  background: transparent;
  color: var(--ads-brand);
  border: 1px solid var(--ads-brand);
  border-radius: var(--ads-radius-sm);
  font-family: "Open Sans", sans-serif;
  font-weight: 600;
}
.theme-ads .filter-btn:hover {
  background: var(--ads-brand-wash);
}
.theme-ads .filter-btn.is-active {
  background: var(--ads-brand-wash);
  color: var(--ads-brand);
}
.theme-ads .search__input {
  background: var(--ads-surface);
  border: 1px solid var(--ads-border-default);
  border-radius: var(--ads-radius-pill);
  padding: 0 36px 0 16px;
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  color: var(--ads-text-primary);
  box-shadow: none;
}
.theme-ads .search__input:hover {
  border-color: var(--ads-border-bold);    /* search/border/bold */
  box-shadow: none;
}
.theme-ads .search__input:focus {
  border-color: var(--ads-brand);
  box-shadow: 0 0 0 2px rgba(64, 69, 194, 0.20);
  outline: none;
}
.theme-ads .search__input::placeholder {
  color: var(--ads-text-placeholder);
}
.theme-ads .search__icon { color: var(--ads-text-tertiary); }

/* ---------------------------------------------------------------------------
 *  TABLE HEADER  (Figma 7737:210, Table Header Cell 105:9)
 *  Opaque rest surface, Open Sans Regular 13/18, 10px vertical and 12px
 *  horizontal padding, 6px label gap, and border/default divider.
 * ------------------------------------------------------------------------ */
.theme-ads .table__head {
  min-height: calc(
    var(--ads-table-header-line-height)
    + var(--ads-table-header-padding-y)
    + var(--ads-table-header-padding-y)
    + var(--ads-input-border-width)
  );
  height: auto;
  background: var(--ads-surface);
  border-bottom: var(--ads-input-border-width) solid var(--ads-border-default);
}
.theme-ads .th {
  align-self: stretch;
  gap: var(--ads-table-header-gap);
  padding:
    var(--ads-table-header-padding-y)
    var(--ads-table-header-padding-x);
  font-family: "Open Sans", sans-serif;
  font-weight: var(--ads-table-header-font-weight);
  font-size: var(--ads-table-header-font-size);
  line-height: var(--ads-table-header-line-height);
  letter-spacing: 0;
  text-transform: none;
  color: var(--ads-table-header-text);
}
.theme-ads .th--sortable {
  gap: var(--ads-table-header-gap);
  padding:
    var(--ads-table-header-padding-y)
    var(--ads-table-header-padding-x);
}
.theme-ads .th__sort {
  color: #9CA0A8;
}
/* Active sort state in the ADS theme: stronger label color + weight so the
 * sorted column stands out. Only the active direction gets the brand color;
 * the inactive direction stays neutral so both arrows remain visible. */
.theme-ads .th.th--sort-asc,
.theme-ads .th.th--sort-desc {
  font-weight: 600;
  color: var(--ads-text-primary, #1E2528);
}
.theme-ads .th.th--sort-asc .th__sort .th__sort-direction--ascending,
.theme-ads .th.th--sort-desc .th__sort .th__sort-direction--descending {
  color: var(--ads-brand);
}

/* ---------------------------------------------------------------------------
 *  TABLE ROWS  (1727:13167 rest, 1727:13174 hover, 1727:13181 selected)
 *  Cell 14/20 text/primary, padding 12/12 gap 8.
 *  Hover bg black/op/5. Selected bg brand-wash. Divider black/op/10.
 * ------------------------------------------------------------------------ */
.theme-ads .row {
  /* ADS Table rule per ads-components.json:
   *   rowOpacity: "opaque (color/surface/table/rest aliases to surface/default)"
   * Bind row background to the surface token so rows never go transparent,
   * which is required for the sticky-header phase-2 work and for any future
   * non-white container chrome. */
  background: var(--ads-surface);
  border-bottom: 1px solid var(--ads-border-recessed);
}
/* Row hover is a subtle gray surface tint - helps the eye track a row
 * across the wide table. DO NOT add cursor:pointer to the row: only
 * actual interactive elements (Name link, Action icons, sortable
 * headers, pagination, dropdowns, buttons) carry pointer cursor.
 * Row click is NOT clickable as a whole - per the simplified
 * interaction brief there is no Details slideover, the Name link
 * is the primary entry into Edit, and the Action column owns
 * secondary actions. */
.theme-ads .row:hover { background: var(--ads-surface-hover); }
.theme-ads .row.is-selected { background: var(--ads-brand-wash); }

/* Sticky Action column has to stay opaque so cells passing under it
 * during horizontal scroll don't bleed through (see .row .actions
 * base rule for the stickiness). But that opaque white base is what
 * paints the "white pill" behind the action icons on a hovered or
 * selected row: the rest of the row picks up its tint via the two
 * rules above while the sticky column stays pure #FFFFFF, so the
 * icon cluster reads as sitting on a card-like surface.
 *
 * Fix by compositing the row's tint on top of the sticky column's
 * opaque base with a linear-gradient overlay: the column still
 * hides scrolled cells but now visually matches the hovered/selected
 * row. Selected uses the opaque --ads-brand-wash directly (already
 * fully opaque, no composite needed). */
.theme-ads .row:hover .actions {
  background:
    linear-gradient(var(--ads-surface-hover), var(--ads-surface-hover)),
    var(--surface);
}
.theme-ads .row.is-selected .actions {
  background: var(--ads-brand-wash);
}
.theme-ads .cell {
  padding-inline: var(--ads-table-header-padding-x);
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  line-height: 20px;
  color: var(--ads-text-primary);
}
/* ADS Regular/T20 link spec for the Name column:
 *   color: var(--color-indigo-300, #4045C2)
 *   font-family: InspireTWDC
 *   font-size: 14px
 *   font-weight: 400  (NOT bold/600)
 *   line-height: 21px
 *   font-feature-settings: 'liga' off, 'clig' off
 * Bold + Open Sans was an earlier ADS-theme override that didn't match
 * the canonical ADS Regular/T20 link spec; restored here so every row
 * inherits the same resting style. */
.theme-ads .name__link {
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  font-weight: 400;
  line-height: 20px;
  color: var(--ads-brand);
  font-feature-settings: normal;
}
.theme-ads .name__link:hover {
  color: var(--ads-brand-hover);
  text-decoration: underline;
}
/* Code columns inherit Open Sans in ADS theme, just slightly tighter so
 * the long identifiers stay readable but visually quieter than Name. */
.theme-ads .cell--code {
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  line-height: 19px;
  color: var(--ads-text-primary);
}
.theme-ads .cell--ver {
  font-variant-numeric: tabular-nums;
  color: var(--ads-text-primary);
}

/* ---------------------------------------------------------------------------
 *  CHIPS  (ADS Chip component, Figma 137:3432)
 *  ADS theme reuses the same chip tokens as the EDL fallback. The base
 *  Chip uses the project's InspireTWDC font + the EDL Indigo-70 brand color
 *  per the reference table (see comment block above the .chip rule). The
 *  ADS theme inherits the base .chip rule with no font override - the
 *  reference table chip is the visual source of truth in both themes. */

/* ---------------------------------------------------------------------------
 *  ROW ACTION ICONS
 *  Indigo/300 brand-color icon glyph. Hover/focus do NOT paint a
 *  background fill (the action column is already white + sticky, and
 *  a hover wash reads as a hard white square on top of any hovered
 *  row tint). Delete is the one semantic exception: hover -> red.
 * ------------------------------------------------------------------------ */
.theme-ads .icon-btn {
  color: var(--ads-brand);
  border-radius: var(--ads-radius-sm);
}

/* Icon buttons OUTSIDE the table (filter panel, etc.) keep the
 * standard ADS hover wash for affordance on neutral surfaces. */
.theme-ads .icon-btn:hover {
  background: var(--ads-surface-hover);
  color: var(--ads-brand);
}

/* TABLE ROW action icons: no hover background. The sticky white
 * action column + a 5% black hover wash painted a visible white-ish
 * pill behind every icon, which read as a UI artifact instead of an
 * intentional affordance. Replace with a subtle brand-hover color
 * shift so the hover state is still felt without the box.
 *
 * Specificity note: this rule has to outrank the generic
 * `.theme-ads .icon-btn:hover` rule above; both have the same number
 * of class selectors, but this one comes later in source so it wins
 * with no !important. */
.theme-ads .row .actions .icon-btn,
.theme-ads .row .actions .icon-btn:hover,
.theme-ads .row .actions .icon-btn:focus,
.theme-ads .row .actions .icon-btn:active {
  background: transparent;
  box-shadow: none;
}
.theme-ads .row .actions .icon-btn:hover {
  color: var(--ads-brand-hover);   /* lighten brand on hover */
}
/* Keyboard focus stays visible (WCAG 2.4.7) - use the same indigo
 * outline as the base .icon-btn:focus-visible rule. Outline + offset
 * give a crisp ring without a fill, matching ADS focus-ring guidance. */
.theme-ads .row .actions .icon-btn:focus-visible {
  outline: 2px solid var(--ads-brand);
  outline-offset: -1px;
  background: transparent;
}

/* Delete is the one semantic exception across both contexts: hover
 * shifts the icon to the destructive red. NO background fill in the
 * table row context (same rule as the brand icons above).
 * Attribute selectors match both the standalone "Delete" label and
 * the table-row "Delete rate card" label so the destructive cue
 * applies wherever the trash icon appears. */
.theme-ads .icon-btn[aria-label="Delete"]:hover,
.theme-ads .icon-btn[aria-label^="Delete"]:hover { color: var(--ads-danger-icon); }
.theme-ads .row .actions .icon-btn[aria-label^="Delete"]:hover {
  color: var(--ads-danger-icon);
  background: transparent;
}

/* Sort glyph: re-tint with gray/60 to match ADS muted header text.
 * Scoped to inactive headers only via :not() so the active asc/desc
 * indigo glyphs defined above still apply in the ADS theme. */
/* ADS theme sort glyphs are owned by the base .th__sort rule above
 * (inline SVG injected by JS renderSortIndicators, fill driven by
 * currentColor). The prior background-image stroke-arrow paths used
 * to live here but were stale leftovers from before the inline-SVG
 * switch - they were more specific than the base rule (.theme-ads
 * .th__sort beats .th__sort) so they stomped the new SVG and the
 * user saw mismatched-looking glyphs. Removed entirely. */

/* ---------------------------------------------------------------------------
 *  PAGINATION  (2140:46 digit-rest, 2140:52 digit-active)
 *  Item 36×36, padding 8. Rest: bg white, text #1E2528, border 20% black.
 *  Active: bg indigo/300, text white, border bold-black.
 * ------------------------------------------------------------------------ */
.theme-ads .footer {
  background: var(--ads-surface);
  border-top: 1px solid var(--ads-border-recessed);
  border-bottom: none;
}
/* ADS Text Box / Select per Figma 129:5818 + 129:5820:
 * border 1px #B6C4D3 (Gray/30), radius 8, white surface, Open Sans body. */
.theme-ads .footer select {
  font-family: "Open Sans", sans-serif;
  border: 1px solid #B6C4D3;
  border-radius: 8px;
  background-color: var(--ads-surface);
  color: var(--ads-text-primary);
}
/* ADS Pagination chrome per Figma 142:4070. Rest cells are 36x36 with a
 * 1px rgba(15,18,20,0.2) border (ads/color/border/default). Active cell
 * flips to ads/brand fill + white text + 1px rgba(15,18,20,0.5) border. */
.theme-ads .page-btn {
  color: var(--ads-text-primary);
  font-family: "Open Sans", sans-serif;
  background: var(--ads-surface);
  border: 1px solid var(--ads-border-default);
  border-radius: 8px;
}
.theme-ads .page-btn:hover {
  color: var(--ads-text-primary);
  background: var(--ads-surface-hover);
}
.theme-ads .page-btn.is-active {
  color: var(--ads-text-on-primary);
  background: var(--ads-brand);
  border-color: var(--ads-border-bold);
}
.theme-ads .page-ellipsis {
  /* Ellipsis variant has no border or fill. */
  background: transparent;
  border: 0;
}

/* ---------------------------------------------------------------------------
 *  FILTER SIDE PANEL  (Sheet component 598:1380)
 *  bg white, title 18/24 #1E2528, divider 20% black, padding-x 24, header
 *  padding-y 20, body padding-y 16, shadow elevation/30.
 * ------------------------------------------------------------------------ */
.theme-ads .fpanel {
  background: var(--ads-surface);
  box-shadow: var(--ads-shadow-sheet);
}
.theme-ads .fpanel__header {
  border-top: none;
  border-bottom: 1px solid var(--ads-border-default);
}
.theme-ads .fpanel__title {
  font-family: "Open Sans", sans-serif;
  font-weight: 700;
  font-size: 18px;
  line-height: 24px;
  color: var(--ads-text-primary);
}
.theme-ads .fpanel__reset {
  font-family: "Open Sans", sans-serif;
  color: var(--ads-brand);
  font-weight: 600;
}

/* Field labels + selects use the Dropdown/Select token chain. Select
 * trigger: radius 6, padding 6/12 gap 8, border default, focus 2px brand. */
.theme-ads .ffield__label {
  font-family: "Open Sans", sans-serif;
  font-weight: 600;
  font-size: 14px;
  line-height: 18px;
  color: var(--ads-text-primary);          /* field-label/state/default/text */
}
.theme-ads .ffield__input {
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  border: 1px solid var(--ads-border-default);
  border-radius: var(--ads-radius-sm);
  padding: 0 36px 0 12px;
  color: var(--ads-text-primary);
}
.theme-ads .ffield__input:hover {
  border-color: var(--ads-border-bold);
}
.theme-ads .ffield__input:focus-visible {
  border-color: var(--ads-brand);
  box-shadow: 0 0 0 2px rgba(64, 69, 194, 0.20);
}
.theme-ads .ffield__input.is-placeholder {
  color: var(--ads-text-placeholder);
}
.theme-ads .fpanel__footer {
  background: var(--ads-surface);
  border-top: 1px solid var(--ads-border-default);
  box-shadow: none;
}
.theme-ads .fpanel__footer .btn--secondary {
  background: transparent;
  color: var(--ads-brand);
  border: 1px solid var(--ads-brand);
  border-radius: var(--ads-radius-sm);
  padding: 7px 12px;
}
.theme-ads .fpanel__footer .btn--primary {
  background: var(--ads-brand);
  color: var(--ads-text-on-primary);
  border: 1px solid var(--ads-brand);
  border-radius: var(--ads-radius-sm);
  padding: 8px 12px;
}
.theme-ads .fpanel__footer .btn--primary:hover {
  background: var(--ads-brand-hover);
  border-color: var(--ads-brand-hover);
}

/* ---------------------------------------------------------------------------
 *  MODAL  (Figma node 38:24 - Default 480, radius 12 / 2xl)
 *  Header title 18/24 SemiBold text/primary, divider 10% black,
 *  body 14/20 text/secondary, padding 24, scrim black 50%.
 *
 *  The .modal--ads variant in the main MODAL block above owns the
 *  canonical layout end-to-end so these legacy theme-ads overrides
 *  are scoped to NON-ADS modals only (i.e. anything still using the
 *  old .modal__head + .modal__icon pattern). Once every modal flips
 *  to .modal--ads, this whole block can be deleted.
 * ------------------------------------------------------------------------ */
.theme-ads .modal__scrim {
  background: var(--ads-scrim);
}
.theme-ads .modal:not(.modal--ads) .modal__panel {
  background: var(--ads-surface);
  border-radius: var(--ads-radius-md);
  border: 1px solid var(--ads-border-recessed);
  box-shadow: 0 16px 40px rgba(15, 18, 20, 0.30);
}
.theme-ads .modal:not(.modal--ads) .modal__title {
  font-family: "Open Sans", sans-serif;
  font-weight: 700;
  font-size: 18px;
  line-height: 24px;
  color: var(--ads-text-primary);
}
.theme-ads .modal:not(.modal--ads) .modal__body,
.theme-ads .modal:not(.modal--ads) .modal__target {
  font-family: "Open Sans", sans-serif;
  color: var(--ads-text-secondary);
}

/* ---------------------------------------------------------------------------
 *  POPOVER / DROPDOWN MENU  (Popover 2693:20 + Dropdown 36:12)
 *  bg surface/overlay (white), border 1px 20% black, radius 6, shadow
 *  elevation/30 (y=4 blur=12).
 * ------------------------------------------------------------------------ */
.theme-ads .dropdown__menu {
  background: var(--ads-surface);
  border: 1px solid var(--ads-border-default);
  border-radius: var(--ads-radius-sm);
  box-shadow: var(--ads-shadow-popover);
}
.theme-ads .dropdown__item { font-family: "Open Sans", sans-serif; }
.theme-ads .dropdown__item:hover { background: var(--ads-surface-hover); }
.theme-ads .dropdown__label { color: var(--ads-text-primary); font-weight: 600; }
.theme-ads .dropdown__desc  { color: var(--ads-text-secondary); }

/* ADS Dropdown 36:56, shared Default and Small wrappers. */
.theme-ads .ads-dd {
  --ads-dd-menu-gap: var(--ads-dropdown-menu-gap);
  position: relative;
  gap: var(--ads-label-gap);
  min-width: 0;
  font-family: "Open Sans", sans-serif;
}

.theme-ads .ads-dd__label {
  color: var(--ads-text-primary);
  font-weight: 400;
}

.theme-ads .ads-dd__trigger {
  box-sizing: border-box;
  width: 100%;
  height: auto;
  background: var(--ads-surface);
  border: var(--ads-input-border-width) solid var(--ads-dropdown-border-rest);
  border-radius: var(--ads-radius-sm);
  color: var(--ads-text-secondary);
  font-family: "Open Sans", sans-serif;
  box-shadow: none;
}

.theme-ads .ads-dd--default .ads-dd__label {
  font-size: var(--ads-dropdown-small-font-size);
  line-height: var(--ads-label-line);
}

.theme-ads .ads-dd--default .ads-dd__trigger {
  padding: var(--ads-dropdown-default-pad-y) var(--ads-dropdown-default-pad-x);
  font-size: var(--ads-dropdown-default-font-size);
  line-height: var(--ads-dropdown-default-line-height);
}

.theme-ads .ads-dd--small .ads-dd__label {
  font-size: var(--ads-label-size);
  line-height: var(--ads-label-line);
}

.theme-ads .ads-dd--small .ads-dd__trigger {
  padding: var(--ads-dropdown-small-pad-y) var(--ads-dropdown-small-pad-x);
  font-size: var(--ads-dropdown-small-font-size);
  line-height: var(--ads-label-line);
}

.theme-ads .ads-dd__trigger:hover {
  background: var(--ads-surface);
  border-color: var(--ads-dropdown-border-hover);
}

.theme-ads .ads-dd__trigger:focus-visible {
  border-width: var(--ads-input-border-active);
  border-color: var(--ads-input-border-active-color);
  outline: none;
}

.theme-ads .ads-dd--default .ads-dd__trigger:focus-visible,
.theme-ads .ads-dd--default .ads-dd__trigger[aria-expanded="true"] {
  padding:
    calc(var(--ads-dropdown-default-pad-y) - var(--ads-input-border-width))
    calc(var(--ads-dropdown-default-pad-x) - var(--ads-input-border-width));
}

.theme-ads .ads-dd--small .ads-dd__trigger:focus-visible,
.theme-ads .ads-dd--small .ads-dd__trigger[aria-expanded="true"] {
  padding:
    calc(var(--ads-dropdown-small-pad-y) - var(--ads-input-border-width))
    calc(var(--ads-dropdown-small-pad-x) - var(--ads-input-border-width));
}

.theme-ads .ads-dd__trigger[aria-expanded="true"] {
  border-width: var(--ads-input-border-active);
  border-color: var(--ads-dropdown-border-open);
}

.theme-ads .ads-dd__value--placeholder {
  color: var(--ads-text-placeholder);
}

.theme-ads .ads-dd__value--selected,
.theme-ads .ads-dd__caret {
  color: var(--ads-text-primary);
}

.theme-ads .ads-dd__caret {
  display: grid;
  flex: 0 0 auto;
  place-items: center;
}

.theme-ads .ads-dd--default .ads-dd__caret {
  width: var(--ads-dropdown-default-caret-box);
  height: var(--ads-dropdown-default-caret-box);
}

.theme-ads .ads-dd--default .ads-dd__caret img {
  width: var(--ads-dropdown-default-caret-width);
  height: var(--ads-dropdown-default-caret-height);
}

.theme-ads .ads-dd--small .ads-dd__caret {
  width: var(--ads-dropdown-small-caret-box);
  height: var(--ads-dropdown-small-caret-box);
}

.theme-ads .ads-dd--small .ads-dd__caret img {
  width: var(--ads-dropdown-small-caret-width);
  height: var(--ads-dropdown-small-caret-height);
}

.theme-ads .ads-dd__caret img {
  display: block;
}

.theme-ads .ads-dd__menu {
  position: absolute;
  top: var(--ads-dd-menu-top);
  left: 0;
  width: 100%;
  min-width: 100%;
  margin: 0;
  padding-block: var(--ads-dropdown-menu-gap);
  overflow-y: auto;
  background: var(--ads-dropdown-menu-surface);
  border: var(--ads-input-border-width) solid var(--ads-dropdown-menu-border);
  border-radius: var(--ads-radius-sm);
  box-shadow: var(--ads-shadow-popover);
}

.theme-ads .ads-dd__option {
  min-height: var(--ads-option-height);
  padding: var(--ads-option-pad-y) var(--ads-option-pad-x);
  color: var(--ads-text-primary);
  font-family: "Open Sans", sans-serif;
  font-weight: 400;
  white-space: normal;
  overflow-wrap: anywhere;
}

.theme-ads .ads-dd--default .ads-dd__option {
  font-size: var(--ads-dropdown-default-font-size);
  line-height: var(--ads-dropdown-default-line-height);
}

.theme-ads .ads-dd--small .ads-dd__option {
  font-size: var(--ads-dropdown-small-font-size);
  line-height: var(--ads-label-line);
}

.theme-ads .ads-dd__option:hover,
.theme-ads .ads-dd__option.is-selected {
  background: var(--ads-dropdown-option-state);
}

.theme-ads .ads-dd__option:active,
.theme-ads .ads-dd__option:focus-visible {
  background: var(--ads-surface-active);
}

.theme-ads .ads-dd__option.is-disabled {
  color: var(--ads-text-disabled);
  cursor: default;
}

.theme-ads .ads-dd:has(.ads-dd__trigger:disabled) {
  opacity: var(--ads-control-disabled-opacity);
}

.theme-ads .ads-dd__trigger:disabled {
  border-color: var(--ads-dropdown-border-disabled);
  color: var(--ads-input-text-disabled);
  cursor: default;
}

.theme-ads .field.is-invalid .ads-dd__trigger {
  border-color: var(--ads-input-border-error);
}

.theme-ads .ads-dd__native[hidden] {
  display: none;
}

/* Compatibility host for legacy routes, rendered with ADS Dropdown chrome. */
.theme-ads .ads-select.edl-select {
  position: relative;
  min-width: 0;
  font-family: "Open Sans", sans-serif;
}

.theme-ads .ads-select .edl-select__trigger {
  box-sizing: border-box;
  width: 100%;
  height: auto;
  background: var(--ads-surface);
  border: var(--ads-input-border-width) solid var(--ads-dropdown-border-rest);
  border-radius: var(--ads-radius-sm);
  color: var(--ads-text-secondary);
  font-family: "Open Sans", sans-serif;
  box-shadow: none;
}

.theme-ads .ads-select--default .edl-select__trigger {
  padding: var(--ads-dropdown-default-pad-y) var(--ads-dropdown-default-pad-x);
  font-size: var(--ads-dropdown-default-font-size);
  line-height: var(--ads-dropdown-default-line-height);
}

.theme-ads .ads-select--small .edl-select__trigger {
  padding: var(--ads-dropdown-small-pad-y) var(--ads-dropdown-small-pad-x);
  font-size: var(--ads-dropdown-small-font-size);
  line-height: var(--ads-label-line);
}

.theme-ads .ads-select .edl-select__trigger:hover {
  background: var(--ads-surface);
  border-color: var(--ads-dropdown-border-hover);
}

.theme-ads .ads-select .edl-select__trigger:focus-visible {
  border-width: var(--ads-input-border-active);
  border-color: var(--ads-input-border-active-color);
  outline: none;
}

.theme-ads .ads-select--default .edl-select__trigger:focus-visible,
.theme-ads .ads-select--default .edl-select__trigger[aria-expanded="true"] {
  padding:
    calc(var(--ads-dropdown-default-pad-y) - var(--ads-input-border-width))
    calc(var(--ads-dropdown-default-pad-x) - var(--ads-input-border-width));
}

.theme-ads .ads-select--small .edl-select__trigger:focus-visible,
.theme-ads .ads-select--small .edl-select__trigger[aria-expanded="true"] {
  padding:
    calc(var(--ads-dropdown-small-pad-y) - var(--ads-input-border-width))
    calc(var(--ads-dropdown-small-pad-x) - var(--ads-input-border-width));
}

.theme-ads .ads-select .edl-select__trigger[aria-expanded="true"] {
  border-width: var(--ads-input-border-active);
  border-color: var(--ads-dropdown-border-open);
  box-shadow: none;
}

.theme-ads .ads-select .edl-select__value {
  color: var(--ads-text-secondary);
  font: inherit;
}

.theme-ads .ads-select .edl-select__value--placeholder {
  color: var(--ads-text-placeholder);
}

.theme-ads .ads-select .edl-select__chev {
  flex: 0 0 auto;
  color: var(--ads-text-primary);
  background: center / contain no-repeat url("./assets/ads-dropdown-caret-default.svg");
  transform: none;
}

.theme-ads .ads-select--default .edl-select__chev {
  width: var(--ads-dropdown-default-caret-width);
  height: var(--ads-dropdown-default-caret-height);
  margin-inline: calc(
    (var(--ads-dropdown-default-caret-box) - var(--ads-dropdown-default-caret-width)) / 2
  );
}

.theme-ads .ads-select--small .edl-select__chev {
  width: var(--ads-dropdown-small-caret-width);
  height: var(--ads-dropdown-small-caret-height);
  margin-inline: calc(
    (var(--ads-dropdown-small-caret-box) - var(--ads-dropdown-small-caret-width)) / 2
  );
  background-image: url("./assets/ads-dropdown-caret.svg");
}

.theme-ads .ads-select .edl-select__chev path {
  display: none;
}

.theme-ads .ads-select .edl-select__trigger[aria-expanded="true"] .edl-select__chev {
  color: var(--ads-text-primary);
  transform: rotate(180deg);
}

.theme-ads .ads-select .edl-select__menu {
  padding-block: var(--ads-dropdown-menu-gap);
  overflow-y: auto;
  background: var(--ads-dropdown-menu-surface);
  border: var(--ads-input-border-width) solid var(--ads-dropdown-menu-border);
  border-radius: var(--ads-radius-sm);
  box-shadow: var(--ads-shadow-popover);
}

.theme-ads .ads-select .edl-select__option {
  height: auto;
  min-height: var(--ads-option-height);
  padding: var(--ads-option-pad-y) var(--ads-option-pad-x);
  gap: 0;
  color: var(--ads-text-primary);
  font-family: "Open Sans", sans-serif;
  font-weight: 400;
  white-space: normal;
  overflow-wrap: anywhere;
}

.theme-ads .ads-select--default .edl-select__option {
  font-size: var(--ads-dropdown-default-font-size);
  line-height: var(--ads-dropdown-default-line-height);
}

.theme-ads .ads-select--small .edl-select__option {
  font-size: var(--ads-dropdown-small-font-size);
  line-height: var(--ads-label-line);
}

.theme-ads .ads-select .edl-select__option:hover,
.theme-ads .ads-select .edl-select__option.is-active,
.theme-ads .ads-select .edl-select__option.is-selected {
  background: var(--ads-dropdown-option-state);
  color: var(--ads-text-primary);
}

.theme-ads .ads-select .edl-select__option::before {
  display: none;
}

.theme-ads .field.is-invalid .ads-select .edl-select__trigger {
  border-color: var(--ads-input-border-error);
}

/* ---------------------------------------------------------------------------
 *  PROFILE MENU + THEME SUBMENU (uses Popover surface tokens)
 * ------------------------------------------------------------------------ */
.theme-ads .profile-menu {
  background: var(--ads-surface);
  border: 1px solid var(--ads-border-default);
  border-radius: var(--ads-radius-sm);
  box-shadow: var(--ads-shadow-popover);
}
.theme-ads .profile-menu__name  { font-family: "Open Sans", sans-serif; font-weight: 700; color: var(--ads-text-primary); }
.theme-ads .profile-menu__email { font-family: "Open Sans", sans-serif; color: var(--ads-text-secondary); }
.theme-ads .profile-menu__item  { font-family: "Open Sans", sans-serif; color: var(--ads-text-primary); }
.theme-ads .profile-menu__item:hover { background: var(--ads-surface-hover); }
.theme-ads .profile-menu__sub-wrap.is-open > .profile-menu__item--has-submenu {
  color: var(--ads-brand);
  background: var(--ads-surface-hover);
}
.theme-ads .theme-submenu {
  background: var(--ads-surface);
  border: 1px solid var(--ads-border-default);
  border-radius: var(--ads-radius-sm);
  box-shadow: var(--ads-shadow-popover);
}
.theme-ads .theme-submenu__item { font-family: "Open Sans", sans-serif; color: var(--ads-text-primary); }
.theme-ads .theme-submenu__item:hover { background: var(--ads-surface-hover); }
.theme-ads .theme-submenu__item--active { color: var(--ads-brand); }

/* Cell padding + grid template are NOT overridden. Keeping layout identical
 * between themes guarantees the same shared row renderer produces the
 * same column alignment in both EDL Light and Ad Design System. */

/* ============================================================================
 * PROFILE MENU + THEME SUBMENU (EDL-style)
 * ============================================================================
 * Scoped entirely under .profile-menu / .theme-submenu so it cannot affect
 * the Import CSV dropdown, the filter panel, or any other surface. Uses the
 * existing [data-dropdown] machinery (closeAllPopovers / Escape handler) so
 * we get click-outside and Esc closing without writing new event plumbing.
 *
 * Anchor: the menu's parent (.gnav__profile-wrap) is position:relative; the
 * menu is absolutely positioned to drop down-and-leftward from the avatar's
 * right edge. The Theme submenu pops out to the LEFT of the main menu so it
 * matches the reference screenshot (and avoids running off the right edge).
 * ========================================================================== */

.gnav__profile-wrap {
  position: relative;
  display: inline-flex;
  height: auto;
  align-items: center;
}

/* Main profile menu surface — white rounded card, subtle indigo-tinted
 * border, soft drop shadow per EDL menu spec. ~260px wide so the email line
 * fits without wrapping. */
.profile-menu {
  position: absolute;
  top: calc(100% + var(--navigation-nav-tabs-gap)); /* gap below the avatar */
  right: 0;                         /* anchor to avatar's right edge */
  width: 260px;
  padding: 4px 0;                   /* header/footer items provide their own y-padding */
  background: var(--surface);
  border: 1px solid var(--gray-20);
  border-radius: 8px;
  /* Override the .dropdown__menu shared rule (which uses overflow:hidden to
   * clip rounded corners for Import CSV) — the Theme submenu needs to render
   * OUTSIDE the menu's bounds. Row hover backgrounds don't bleed past the
   * rounded corners because rows have no background in their default state. */
  overflow: visible;
  box-shadow:
    0 8px 24px rgba(23, 29, 35, 0.12),
    0 2px 6px rgba(23, 29, 35, 0.06);
  z-index: 50;                      /* above table card (z=1) and below modal scrim (z=100) */
  font-size: 14px;
  line-height: 21px;
  color: var(--gray-95);
}

/* Header — user name + email block */
.profile-menu__header {
  padding: 12px 16px 10px;
}
.profile-menu__name {
  font-weight: 600;
  font-size: 14px;
  line-height: 21px;
  color: var(--gray-95);
}
.profile-menu__email {
  margin-top: 2px;
  font-weight: 400;
  font-size: 14px;
  line-height: 18px;
  color: var(--gray-50);
  /* Keep the email on a single line; ellipsize if a future longer email is set. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.profile-menu__divider {
  height: 1px;
  background: var(--gray-20);
  margin: 4px 0;
}

/* Menu rows — button reset + flex layout: label left, chevron right. */
.profile-menu__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  height: 40px;
  padding: 0 16px;
  background: transparent;
  border: 0;
  font: inherit;
  text-align: left;
  color: var(--gray-95);
  cursor: pointer;
}
.profile-menu__item:hover,
.profile-menu__item:focus-visible {
  background: var(--gray-05);
  outline: none;
}
.profile-menu__item:focus-visible {
  /* Use a contained focus ring so the row's background stays clean. */
  box-shadow: inset 0 0 0 2px var(--indigo-70);
  border-radius: 0;
}
.profile-menu__chevron {
  display: block;
  width: 16px;
  height: 16px;
  color: var(--gray-50);
  flex-shrink: 0;
}

/* Presentation view row (2026-08-04). Adds a leading 16px icon while
 * inheriting the shared .profile-menu__item chrome (40px row height,
 * 16px horizontal padding, hover / focus states). The wrapper span
 * lets the icon and label read as a single left-aligned unit while
 * the parent .profile-menu__item keeps its space-between rule for
 * future trailing content (e.g. keyboard hint), matching the pattern
 * used by Version, Theme, and Redline. */
.profile-menu__item-label {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.profile-menu__item-icon {
  display: block;
  width: 16px;
  height: 16px;
  color: var(--gray-70, var(--gray-50));
  flex-shrink: 0;
}
.profile-menu__item:hover .profile-menu__item-icon,
.profile-menu__item:focus-visible .profile-menu__item-icon {
  color: var(--gray-95);
}
.profile-menu__item--presentation[aria-disabled="true"],
.profile-menu__item--presentation[disabled] {
  color: var(--gray-50);
  cursor: not-allowed;
}
.profile-menu__item--presentation[aria-disabled="true"] .profile-menu__item-icon,
.profile-menu__item--presentation[disabled] .profile-menu__item-icon {
  color: var(--gray-50);
}
.profile-menu__item--presentation[aria-disabled="true"]:hover,
.profile-menu__item--presentation[disabled]:hover {
  background: transparent;
}
/* Keep the parent row visually "active" while its submenu is open so the
 * relationship is clear (matches typical EDL menu behavior). */
.profile-menu__sub-wrap.is-open > .profile-menu__item--has-submenu {
  background: var(--gray-05);
  color: var(--indigo-70);
}
.profile-menu__sub-wrap.is-open > .profile-menu__item--has-submenu .profile-menu__chevron {
  color: var(--indigo-70);
}

/* Theme submenu — pops to the LEFT of the main menu and aligns vertically
 * with the Theme row (top:0 relative to the .profile-menu__sub-wrap, which
 * is itself a child of the main menu).
 *
 * Width 184px = enough for "Ad Design System" + comfortable padding without
 * stretching unnecessarily. */
.profile-menu__sub-wrap {
  position: relative;
}
.theme-submenu {
  position: absolute;
  top: 0;
  /* Sit to the LEFT of the main menu: width 184 + 8px gap from the menu. */
  right: calc(100% + 8px);
  width: 184px;
  padding: 8px 0;
  background: var(--surface);
  border: 1px solid var(--gray-20);
  border-radius: 8px;
  box-shadow:
    0 8px 24px rgba(23, 29, 35, 0.12),
    0 2px 6px rgba(23, 29, 35, 0.06);
  z-index: 51;
}
.theme-submenu__item {
  display: block;
  width: 100%;
  height: 36px;
  padding: 0 16px;
  background: transparent;
  border: 0;
  font: inherit;
  font-weight: 500;
  text-align: left;
  color: var(--gray-95);
  cursor: pointer;
  line-height: 36px;
}
.theme-submenu__item:hover,
.theme-submenu__item:focus-visible {
  background: var(--gray-05);
  outline: none;
}
.theme-submenu__item:focus-visible {
  box-shadow: inset 0 0 0 2px var(--indigo-70);
}
.theme-submenu__item--active {
  color: var(--indigo-70);
}

/* ===================================================================
 * VERSION submenu (Profile menu > Version > 1.0 | 1.1)
 *
 * Visually identical to the Theme submenu (same affordance pattern,
 * same Open Sans 14/20 SemiBold/Regular split) so the menu reads as
 * a coherent two-row group instead of two different submenu styles.
 * Active row uses the ADS brand color so the selected version is
 * obvious at a glance.
 * =================================================================== */
.version-submenu {
  position: absolute;
  top: 0;
  /* Sit to the LEFT of the main menu, same anchor strategy as theme. */
  right: calc(100% + 8px);
  width: 96px;
  padding: 8px 0;
  background: var(--surface);
  border: 1px solid var(--gray-20);
  border-radius: 8px;
  box-shadow:
    0 8px 24px rgba(23, 29, 35, 0.12),
    0 2px 6px rgba(23, 29, 35, 0.06);
  z-index: 51;
}
.version-submenu__item {
  display: block;
  width: 100%;
  height: 36px;
  padding: 0 16px;
  background: transparent;
  border: 0;
  font: inherit;
  font-weight: 500;
  text-align: left;
  color: var(--gray-95);
  cursor: pointer;
  line-height: 36px;
}
.version-submenu__item:hover,
.version-submenu__item:focus-visible {
  background: var(--gray-05);
  outline: none;
}
.version-submenu__item:focus-visible {
  box-shadow: inset 0 0 0 2px var(--indigo-70);
}
.version-submenu__item--active {
  color: var(--indigo-70);
}
.theme-ads .version-submenu { background: var(--ads-surface); border-color: var(--ads-border-subtle); }
.theme-ads .version-submenu__item { font-family: "Open Sans", sans-serif; color: var(--ads-text-primary); }
.theme-ads .version-submenu__item:hover { background: var(--ads-surface-hover); }
.theme-ads .version-submenu__item--active { color: var(--ads-brand); }

/* ===================================================================
 * VERTICAL SIDE NAVIGATION (Version 1.1 and 1.2)
 *
 * 1:1 rebuild of ADS Figma:
 *   - 796:45  Side Nav, Expanded  (240px wide, 40px row height)
 *   - 796:67  Side Nav, Collapsed (64px wide, 44x44 row)
 *
 * The .vnav element is hidden by default ([hidden] attribute on the
 * <aside>). Activating Version 1.1 or 1.2 (body[data-version="1.1"]
 * or body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"])) reveals the nav, hides the
 * .gnav__items top tabs, and pushes the main page to the right by
 * the nav's width. v1.2 was forked from v1.1 on 2026-07-09 as the
 * "latest" baseline for forward feature work; visually it renders
 * identically to v1.1 today.
 *
 * Tokens (DO NOT inline-recompute - update here). Current source of
 * truth is the Rate Card sidebar pair, Figma 595:6574 (expanded) and
 * 595:6631 (collapsed), which supersedes the older 796:45 / 796:67
 * ADS reference the first implementation was built from:
 *   --vnav-width-expanded     200px  (Figma 595:6574)
 *   --vnav-width-collapsed     68px  (Figma 595:6631)
 *   --vnav-cell-size           40px  (Figma 595:6632 NavItem)
 *   --vnav-pad-x               14px  (rail inset, both sides)
 *   --vnav-pad-y               20px  (inset above the first row)
 *   --vnav-row-radius           6px  (Figma --radius/6)
 *   --vnav-row-gap              4px
 *   --vnav-icon-label-gap       8px
 *   --vnav-active-bg          #E2E3F2 (brand wash)
 *   --vnav-active-text        #4045C2 (brand)
 *   --vnav-rest-text          #3B4347
 *   --vnav-border             #E5E5EB
 *
 * Top bar (.gnav) stays in Version 1.1 but its product-area tabs
 * (Sales/Planning/Ad Ops/Billing/Admin) are hidden because the side
 * nav owns that contract. The brand mark + utility cluster
 * (notification bell, More, avatar) all remain accessible. */
/* ===================================================================
 *  APP-SHELL SIDE NAV  (Version 1.1)
 *
 *  Two-state click-only model (2026-07-08 revision, supersedes the
 *  earlier sticky-open-on-hover design):
 *
 *    collapsed   Narrow 64px icon-only rail, in flow. Page content
 *                starts immediately to the right. Default on first
 *                load.
 *    expanded    240px panel in flow. Page content shifts right to
 *                accommodate it. Entered ONLY by clicking the toggle
 *                button at the bottom of the rail (data-action=
 *                "vnav-toggle"). Clicking the same button again
 *                returns to collapsed. Hover, focus, and clicks
 *                anywhere else on the rail (nav links, brand tile)
 *                do NOT change the state - the sidebar is entirely
 *                user-controlled. Choice persists to localStorage
 *                under "rate-card-nav-pinned" so refreshes and
 *                in-app navigations keep the last chosen state.
 *
 *  isExpanded == body has data-vnav-pinned="1" and .vnav carries
 *  .vnav--pinned. The legacy .vnav--hovered class is kept in CSS
 *  selectors (as an alias) so any lingering code path that still
 *  toggles it doesn't visually break, but new code should only use
 *  .vnav--pinned.
 *
 *  All widths and the active row treatment come from the ADS Figma
 *  references (796:45 + 796:67) plus the brief's compact-rail spec.
 *
 *  Cross-version notes:
 *    - body[data-version="1.1"] (default since 2026-07-08): .vnav is
 *      the primary product-nav surface. First-time visitors land on
 *      this state; v1.0 stays intact and is reachable via the
 *      Profile menu switcher and ?version=1.0.
 *    - body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) (forked 2026-07-09): shares the same
 *      side-rail rendering as v1.1. The page-shell layout rules
 *      below are gated on BOTH tokens so v1.2 opens identically to
 *      v1.1 on first visit; future v1.2-only overrides are added as
 *      new rules that match only body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) so v1.1
 *      stays visually frozen.
 *    - body[data-version="1.0"] (legacy, preserved): .vnav is
 *      hidden, page falls back to the top horizontal nav. All
 *      app-shell CSS below is gated on the v1.1 / v1.2 sibling
 *      selectors so v1.0 stays untouched.
 *    - The .gnav top bar stays in every version. In 1.1 / 1.2 we
 *      hide .gnav__items (the product tabs) because the side nav
 *      owns that contract.
 * =================================================================== */
:root {
  --vnav-width-expanded:   200px;   /* Figma 595:6574 */
  --vnav-width-collapsed:    68px;   /* Figma 595:6631 */
  --vnav-top-offset:        var(--app-header-block-size);
  --vnav-cell-size:          40px;
  --vnav-icon-label-gap:      8px;
  --vnav-pad-y:              20px;
  --navigation-padding-x:   24px;
  --navigation-brand-gap:    8px;
  --navigation-nav-tabs-gap: 4px;
  --navigation-utilities-gap: 16px;
  --font-body-normal-sm-font-size: 14px;
  --font-body-normal-sm-line-height: 18px;
  --font-body-normal-md-font-size: 14px;
  --font-body-normal-md-line-height: 20px;
  --font-body-normal-lg-font-size: 16px;
  --font-body-normal-lg-line-height: 22px;
  --font-body-normal-xl-line-height: 24px;
  /* ADS font/heading/lg, the same token the Accounts Receivable page
   * title uses. The size and line height were previously 28/38, which is
   * heading/xl; nothing consumed them, so they are corrected here rather
   * than duplicated. */
  --font-heading-lg-font-family: "MultiplaneTWDC Display";
  --font-heading-lg-font-size: 24px;
  --font-heading-lg-font-weight: 500;
  --font-heading-lg-line-height: 28px;
  --font-heading-lg-letter-spacing: 0;
}
.vnav {
  /* Figma Rate Card sidebar (595:6574 expanded, 595:6631 collapsed). */
  --vnav-width-expanded:   200px;
  --vnav-width-collapsed:   68px;
  --vnav-cell-size:         40px;
  --vnav-row-radius:         6px;
  --vnav-row-gap:            4px;
  --vnav-icon-label-gap:     8px;
  --vnav-active-bg:        var(--color-surface-brand-wash, #E2E3F2);
  --vnav-active-text:      var(--color-text-brand, #4045C2);
  --vnav-rest-text:        var(--color-text-secondary, #3B4347);
  --vnav-border:           var(--color-border-subtle, #E5E5EB);
  --vnav-toggle-bg:        var(--color-surface-subtle, #F5F5F7);
  --vnav-pad-y:             20px;
  /* Figma 595:6631: the rail is 68px wide with a symmetric 14px inset,
   * so the 40px NavItem lands at x=14..54 and its icon centers on
   * x=34 - the rail's own midline, and the same axis the header
   * logomark centers on (.gnav__brand-icon--v2: 18px + 32/2 = 34).
   * Keep both insets equal: an asymmetric inset slides the icon
   * column off that shared axis and leaves dead space on the other
   * side of the rail. */
  --vnav-pad-x:             14px;
  /* Bottom inset for the collapse-button footer row, independent of
   * the top inset above the nav items (--vnav-pad-y still owns that). */
  --vnav-footer-pad-bottom: 16px;

  position: fixed;
  top: var(--vnav-top-offset);
  left: 0;
  bottom: var(--app-safe-area-bottom);
  /* Default: collapsed rail. JS adds .vnav--pinned when the user
   * hovers, clicks, or tab-focuses into the rail (2026-07-07 sticky-
   * open revision); the rail then stays open until the bottom close
   * button flips it back. The legacy .vnav--hovered class is still
   * matched below so any code path that still adds it keeps working,
   * but new code should not toggle it - the sticky open state is
   * represented by .vnav--pinned alone. Both classes widen the rail. */
  width: var(--vnav-width-collapsed);
  padding:
    var(--vnav-pad-y)
    var(--vnav-pad-x)
    var(--vnav-footer-pad-bottom);
  background: #FFFFFF;
  border-right: 1px solid var(--vnav-border);
  font-family: "Open Sans", "DM Sans", Inter, sans-serif;
  z-index: 40;
  box-sizing: border-box;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  /* Smooth width / shadow animation, 220ms ease-out per the sticky-
   * open brief (target: 200-300ms). ease-out lets the rail decelerate
   * into place so the deliberate open/close feels less mechanical
   * than the previous linear-ish 180ms curve. */
  transition: width 220ms ease-out, box-shadow 220ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .vnav,
  body[data-version="1.1"] .page,
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .page,
  .vnav__link,
  .vnav__label {
    transition: none !important;
  }
}

.vnav__items {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--vnav-row-gap);
  flex: 0 0 auto;
}
.vnav__item {
  width: 100%;
  border-radius: var(--vnav-row-radius);
}
/* Single anchor per row so the whole row (icon + label) is one
 * clickable/keyboard target. In COLLAPSED state the link is the
 * 40x40 Figma NavItem cell (595:6632). It sits flush against the
 * rail's 14px inset, which - because 14 + 40 + 14 == the 68px rail -
 * is also dead center, so its 20px icon lands on x=34 alongside the
 * header logomark. In EXPANDED states (hover or pinned) it grows to
 * full panel width with icon + label. */
.vnav__link {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--vnav-icon-label-gap);
  width: var(--vnav-cell-size);
  height: var(--vnav-cell-size);
  min-height: var(--vnav-cell-size);
  padding: 0;
  justify-content: center;
  margin: 0;
  border-radius: var(--vnav-row-radius);
  color: var(--vnav-rest-text);
  text-decoration: none;
  font-size: var(--font-body-normal-md-font-size, 14px);
  line-height: var(--font-body-normal-md-line-height, 20px);
  font-weight: 400;
  background: transparent;
  transition: background-color 120ms ease-out, color 120ms ease-out;
}
.vnav__link:hover {
  /* ADS NavItem Hover state per the Ads Design System library.
   * navigation/surface/hover = --ads-surface-hover (rgba(15,18,20,0.05))
   * flowed through --gray-05 as the equivalent opaque token so the
   * hover wash is consistent with buttons and dropdowns. */
  background: var(--gray-05);
  color: var(--vnav-active-text);
}
.vnav__link:focus-visible {
  outline: 2px solid var(--vnav-active-text);
  outline-offset: -2px;
}
/* Active row: brand-wash bg + brand text. In collapsed state we drop
 * the 3px indicator bar (brand-wash alone reads). In expanded state
 * the indicator bar shows on the row's left. */
.vnav__item--active > .vnav__link {
  background: var(--vnav-active-bg);
  color: var(--vnav-active-text);
  font-weight: 600;
}
/* Active accent bar - 3px indigo pill pinned to the left edge of the
 * active row. Per the Figma ATLAS NavItem spec + user brief the bar
 * shows in BOTH collapsed and expanded states so the active-state
 * treatment is consistent across the rail's two widths (only the
 * label visibility changes with width). Only the .is-active row
 * paints the bar; inactive rows keep the span in the DOM (for
 * layout stability during a state swap) but leave it transparent. */
.vnav__active-bar {
  flex: 0 0 3px;
  width: 3px;
  height: 20px;
  border-radius: 2px;
  background: transparent;
  display: none;
}
.vnav--pinned .vnav__active-bar {
  display: block;
}
.vnav__item--active .vnav__active-bar {
  background: var(--vnav-active-text);
}
.vnav:not(.vnav--pinned) .vnav__active-bar {
  display: none !important;
}
/* 20x20 ADS icon slot per Figma 595:6725 / 595:6688 ("icon" frame).
 * The <svg> inside carries its own width/height matching its own
 * viewBox exactly (e.g. Orders is 16.25x13.75, Rate cards/Tag is
 * 16.8748x16.8748) per the Figma Vector inset for that specific
 * glyph - never a uniform 20x20. Forcing every glyph to 20x20 here
 * would stretch non-square icons and enlarge square ones past the
 * Figma leaf size, which is exactly the "oversized/heavy" icon bug
 * this rule fixes. max-width/max-height only cap overflow inside the
 * 20x20 slot; they never upscale a smaller glyph. Do NOT add
 * transform:scale()/zoom to compensate for this - fix the SVG's own
 * width/height/viewBox instead. align-items+justify-content keep
 * every glyph optically centered regardless of its own aspect ratio.
 * No border, no background - icons are completely chrome-free. The
 * 20x20 slot itself never shrinks; the 40px .vnav__link row remains
 * the ADS-compliant click target even when the visible glyph is
 * smaller than its slot. */
.vnav__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  flex: 0 0 20px;
  color: currentColor;
  background: transparent;
  border: 0;
}
.vnav__icon svg { display: block; width: auto; height: auto; max-width: 20px; max-height: 20px; }
.vnav__label {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* sr-only by default - shown only in expanded state below. */
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  clip: rect(0,0,0,0);
  border: 0;
}

/* ---- Expanded state ---------------------------------------------------
 * Under the 2026-07-08 click-only revision the sidebar has one expanded
 * state: .vnav--pinned. The bottom toggle button (data-action="vnav-
 * toggle") is the only affordance that moves the rail between the
 * collapsed 64px icon rail and the expanded 240px panel; hover and
 * focus no longer promote it. The legacy .vnav--hovered class is kept
 * in the selector list so any legacy code path that still adds it
 * stays visually consistent, but new code should only toggle
 * .vnav--pinned. */
.vnav--pinned,
.vnav--hovered {
  width: var(--vnav-width-expanded);
}
/* Legacy floating-overlay shadow. Preserved only for the transient
 * .vnav--hovered class in case any code path still adds it; the sticky
 * .vnav--pinned state sits in flow, page content shifts to make room,
 * and no shadow is painted. */
.vnav--hovered:not(.vnav--pinned) {
  box-shadow: 4px 0 16px rgba(23, 29, 35, 0.10);
}
.vnav--pinned .vnav__link,
.vnav--hovered .vnav__link {
  width: 100%;
  height: 40px;
  min-height: 40px;
  padding: 10px 12px;
  justify-content: flex-start;
  margin: 0;
  gap: var(--vnav-icon-label-gap);
}
.vnav--pinned .vnav__label,
.vnav--hovered .vnav__label {
  position: static;
  width: auto;
  height: auto;
  padding: 0;
  margin: 0;
  clip: auto;
  opacity: 1;
  transition: opacity 160ms ease-out;
}
.vnav:not(.vnav--pinned) .vnav__label {
  opacity: 0;
  transition: opacity 120ms ease-out;
}
/* Active accent bar visibility + fill are now defined on the base
 * .vnav__active-bar rule so the collapsed rail also renders the bar
 * (per ADS NavItem canonical spec + the Figma ATLAS reference). The
 * previous rules that toggled display only when .vnav--pinned or
 * .vnav--hovered were removed; the bar is always laid out and only
 * the .vnav__item--active row fills it with the brand color. */
/* Active row in expanded states: SemiBold per Figma 796:45 active spec. */
.vnav--pinned .vnav__item--active > .vnav__link,
.vnav--hovered .vnav__item--active > .vnav__link {
  font-weight: 600;
}

/* ---- Sidebar footer controls (Figma 595:6574 / 595:6631) ------------
 * 2026-08-12 brief: anchor the collapse button to the bottom-right of
 * the sidebar, driven by viewport height rather than nav-item count or
 * table height. .vnav is already `position: fixed; top: <header
 * height>; bottom: <safe area>`, i.e. a full-height column tied to the
 * viewport - the missing piece was a spacer that keeps growing to fill
 * whatever room is left below the nav items in BOTH states.
 * .vnav__expand-target already carries `flex: 1 1 auto`, so instead of
 * removing it from the pinned (expanded) layout with `display: none`
 * (which also removes its layout box and lets .vnav__toggle-row ride
 * up right under the last nav item - the "beside the navigation
 * content" bug), it stays in flow via `visibility: hidden`. That keeps
 * it acting as the bottom spacer in both states while making it
 * unclickable, unfocusable, and hidden from assistive tech once the
 * rail is already expanded (there is nothing left to "expand"). Net
 * effect: items sit at the top, the spacer eats all remaining vertical
 * space, and .vnav__toggle-row (already `justify-content: flex-end`,
 * width: 100%) always lands flush against the sidebar's own bottom
 * padding (--vnav-footer-pad-bottom, ~16px from the viewport edge) and
 * right edge, on any viewport height, and keeps up with body scroll
 * because .vnav itself is position: fixed, not absolute-against-body. */
.vnav__expand-target {
  flex: 1 1 auto;
  min-height: 48px;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  border-radius: var(--vnav-row-radius);
}
.vnav__expand-target:hover {
  background: var(--gray-05);
}
.vnav__expand-target:focus-visible {
  outline: 2px solid var(--vnav-active-text);
  outline-offset: -2px;
}
.vnav--pinned .vnav__expand-target {
  visibility: hidden;
  pointer-events: none;
}
.vnav__toggle-row {
  display: none;
  justify-content: flex-end;
  align-items: center;
  padding-bottom: 0;
  flex-shrink: 0;
  width: 100%;
}
.vnav--pinned .vnav__toggle-row {
  display: flex;
}
.vnav__close {
  display: flex;
  width: 28px;
  height: 28px;
  justify-content: center;
  align-items: center;
  margin: 0;
  background: var(--vnav-toggle-bg);
  border: 0;
  border-radius: 6px;
  color: var(--vnav-rest-text);
  cursor: pointer;
  transition: background-color 120ms ease-out, color 120ms ease-out;
}
.vnav__close:hover {
  background: var(--gray-05);
  color: var(--vnav-active-text);
}
.vnav__close:active {
  background: var(--vnav-active-bg);
  color: var(--vnav-active-text);
}
.vnav__close:focus-visible {
  outline: 2px solid var(--vnav-active-text);
  outline-offset: 2px;
}
.vnav__close svg {
  /* CaretLeft (Figma 142:3994) is a 16px icon slot with a 6.00067x
   * 11.0006 leaf, matching the .vnav__icon fix above: the svg's own
   * width/height carry its true proportions, max-width/max-height
   * only cap it inside the 16px slot. Do not force width/height:16px
   * here again - that stretches the slender chevron into a fat arrow. */
  display: block;
  width: auto;
  height: auto;
  max-width: 16px;
  max-height: 16px;
}

/* ---- Legacy hover trigger zone (2026-07-08: permanently disabled) ---
 * The 6px screen-edge strip that used to promote the rail on
 * pointerenter is kept in the DOM as an inert element so any external
 * code / tests that still query .vnav__hover-zone resolve without
 * errors. JS also sets the [hidden] attribute in setVnavPinned() as
 * a belt-and-braces measure. It never receives pointer events and
 * never paints. Do NOT reintroduce visible geometry here without a
 * new hover-behavior brief. */
.vnav__hover-zone {
  display: none !important;
  pointer-events: none;
}

/* ---- Version 1.1 / 1.2 page-shell layout swap --------------------------
 * Two layout states for the main <main class="page">:
 *
 *   1. body[data-version="1.0"]
 *      No left padding. Top horizontal nav owns navigation.
 *
 *   2. body[data-version="1.1"] / body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) (nav collapsed)
 *      padding-left = collapsed rail width. Page content starts
 *      immediately to the right of the 64px icon rail.
 *
 *   3. body[data-version="1.1"|"1.2"][data-vnav-pinned="1"] (nav expanded)
 *      padding-left = expanded rail width. Page content shifts right
 *      to make room for the in-flow 240px rail.
 *
 *   Under the 2026-07-08 click-only revision the rail flips between
 *   state 2 and state 3 exclusively on toggle-button click; the page
 *   slides once with the rail so the layout never floats above
 *   content or jumps on its own. Pointer position over the rail has
 *   no effect on state.
 *
 *   v1.2 (forked 2026-07-09) shares the app-shell layout with v1.1
 *   by design so a first open of ?version=1.2 renders identically to
 *   v1.1. Every rule below therefore names BOTH tokens as sibling
 *   selectors: v1.1's original selector text is untouched, we only
 *   ADD a v1.2 sibling. That keeps v1.1 rendering byte-identical
 *   while future v1.2-only overrides can be added as new rules
 *   gated on body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) alone. */
body[data-version="1.1"] .gnav__items,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .gnav__items { display: none; }
body[data-version="1.1"] .page,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .page {
  padding-left: var(--vnav-width-collapsed);
  /* Matches the .vnav width transition so the page slides in lock-
   * step with the sidebar when it opens or closes (220ms ease-out
   * per the 2026-07-07 sticky-open brief). */
  transition: padding-left 220ms ease-out;
}
body[data-version="1.1"][data-vnav-pinned="1"] .page,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"])[data-vnav-pinned="1"] .page {
  padding-left: var(--vnav-width-expanded);
}

/* ---- Responsive (1024 / 768) ------------------------------------------
   * Force-unpin at narrow widths so the side nav can never eat half the
   * viewport. The toggle button stays reachable so the user can always
   * re-expand once viewport width grows again. */
@media (max-width: 1023.98px) {
  body[data-version="1.1"][data-vnav-pinned="1"] .page,
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"])[data-vnav-pinned="1"] .page {
    padding-left: var(--vnav-width-collapsed);
  }
  body[data-version="1.1"] .vnav--pinned,
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .vnav--pinned {
    width: var(--vnav-width-collapsed);
  }
  body[data-version="1.1"] .vnav--pinned .vnav__link,
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .vnav--pinned .vnav__link {
    width: var(--vnav-cell-size); height: var(--vnav-cell-size); padding: 0;
    justify-content: center; margin: 0 auto;
  }
  body[data-version="1.1"] .vnav--pinned .vnav__label,
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .vnav--pinned .vnav__label {
    position: absolute; width: 1px; height: 1px; padding: 0;
    margin: -1px; clip: rect(0,0,0,0); border: 0;
  }
  body[data-version="1.1"] .vnav--pinned .vnav__active-bar,
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .vnav--pinned .vnav__active-bar { display: none; }
  /* Toggle button stays visible at narrow widths (2026-07-08 click-
   * only model): it is now the ONLY affordance that changes rail
   * state, so hiding it here would leave the user with no way to
   * open the panel once they collapse it. The button naturally
   * follows the narrow-width layout because .vnav__close inherits
   * .vnav:not(.vnav--pinned) rules (align-self: center + mirrored
   * chevron) via the responsive collapse. */
}

/* ---- Narrow viewport <768 ----
 * The pinned-collapse rule at <1024 already keeps the page padded
 * to the rail width. Nothing extra needed here; the existing
 * .page__actions wrap behavior keeps the Download/Upload/Create
 * buttons reachable. */
@media (max-width: 767.98px) {
  body[data-version="1.1"] .page,
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .page { padding-left: var(--vnav-width-collapsed); }
}

/* ===================================================================
 *  V1.2 PM SEMANTICS (2026-07-09)
 *
 *  Three v1.2-only visual changes gated on body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]):
 *
 *    1. Subtitle copy swap. The .page__subtitle--v11 sibling stays
 *       visible for v1.0 / v1.1; the .page__subtitle--v12 sibling
 *       only paints under v1.2. Both live in the DOM so a version
 *       switch is a pure display flip (no JS text substitution).
 *
 *    2. Table column reorder. v1.2 shows
 *         Status | Rate Card ID | Name | Marketplace | Buying Entity |
 *         SalesHub ID | Last updated | Ver. | Action
 *       The DOM append order stays as v1.1 (see buildRow() +
 *       .table__head in index.html) - we swap grid-template-columns
 *       so the track widths align to the new visual order, then use
 *       CSS `order:` on the per-cell semantic classes so each cell
 *       lands in the correct track. v1.1's original selectors are
 *       untouched; there is no rule below that matches v1.1 at all.
 *
 *    3. No structural or spacing change to the toolbar, pagination,
 *       action icons, or Name column ellipsis behavior. All existing
 *       ADS chrome (elevation, radius, hover/focus, typography)
 *       carries through v1.2 unchanged.
 * =================================================================== */

/* ---- (1) Subtitle copy swap ----------------------------------------- */
.page__subtitle--v12 { display: none; }
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .page__subtitle--v11 { display: none; }
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .page__subtitle--v12 { display: block; }

/* ---- (2) Table column reorder --------------------------------------- *
 * Two things must change together:
 *   (a) grid-template-columns width sequence (so each track has the
 *       width the cell landing in it expects).
 *   (b) `order:` on each cell so DOM-order cells land in the right
 *       visual track.
 *
 * v1.1 visual track sequence:
 *   1 status  2 saleshub  3 rcid  4 name  5 market  6 buyer
 *   7 updated 8 ver       9 action
 *
 * v1.2 visual track sequence (SalesHub moves from track 2 to track 6,
 * everything from Rate Card ID onward shifts left by one):
 *   1 status  2 rcid      3 name  4 market  5 buyer  6 saleshub
 *   7 updated 8 ver       9 action
 */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .table__head,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row {
  grid-template-columns:
    var(--col-status)
    var(--col-rcid)
    var(--col-name-track)
    var(--col-market)
    var(--col-buyer)
    var(--col-saleshub)
    var(--col-updated)
    var(--col-ver)
    var(--col-action);
}

/* Per-cell `order:` values. Every cell (both header .th and body
 * .cell / .name / .actions) needs an explicit order so the
 * source-order fallback (default order: 0) doesn't shuffle unrelated
 * cells if a new column ever gets appended.
 *
 * These selectors target BOTH the header (.th--<name>) and the body
 * (.cell--<name>) with paired rules so header cells and body cells
 * always land in matching tracks. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .th--status,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .cell--status         { order: 1; }
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .th--rate-card-id,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .cell--rate-card-id   { order: 2; }
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .th--name,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .name                 { order: 3; }
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .th--marketplace,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .cell--marketplace    { order: 4; }
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .th--buying-entity,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .cell--buying-entity  { order: 5; }
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .th--saleshub,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .cell--saleshub       { order: 6; }
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .th--last-updated,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .cell--last-updated   { order: 7; }
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .th--ver,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .cell--ver            { order: 8; }
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .th--action,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .actions              { order: 9; }

/* ---- (3) Horizontal scroll + sticky columns, v1.2 only (2026-07-29 brief)
 * ------------------------------------------------------------------
 * User brief: "After making the left sidebar sticky, the horizontal
 * scrollbar disappeared from the data tables." The previous 2026-07-09
 * brief hid the scrollbar and progressively dropped low-priority
 * columns as the viewport shrank; the new brief restores horizontal
 * scrolling and makes the primary identifier columns sticky on the
 * left so users can still see which row they're inspecting while the
 * mid-columns scroll horizontally into view. Action stays sticky on
 * the right (unchanged from 2026-07-08).
 *
 * Root cause of the disappearing scrollbar:
 *   .table-scroll had overflow-x: hidden. When the row exceeded the
 *   container width, the excess columns were invisible with no
 *   scrollbar to reveal them.
 *
 * Fix (v1.2 only, does not touch v1.1):
 *   1. overflow-x: auto on .table-scroll so the scrollbar returns
 *      when the row exceeds the container width.
 *   2. min-width on .table__head and .row equal to the sum of every
 *      column's fixed width + Name floor + row padding. This forces
 *      the row to overflow the container at narrow viewports so the
 *      scrollbar has real distance to scroll.
 *   3. --col-name-track: minmax(320px, 1fr). Name keeps its
 *      ellipsis-and-tooltip pattern (see .name__link rules) but
 *      never collapses below 320px.
 *   4. position: sticky; left: <cumulative> on Status, Rate Card ID,
 *      and Name. Cumulative offsets flow through the same --col-*
 *      tokens the resize handler writes to, so dragging a column
 *      wider automatically updates downstream sticky offsets.
 *   5. Compose row-hover / row-selected backgrounds onto every
 *      sticky cell (mirrors the existing Action-sticky composition,
 *      see line ~5831) so scrolled content stays hidden while the
 *      visible row picks up its full hover/selected chrome.
 *   6. Bump resizer z-index for handles between two sticky columns
 *      (Status, Rate Card ID, Ver) so the neighbor's stacking context
 *      doesn't paint over the drag handle. Matches the existing
 *      Marketplace-resizer fix at line ~7146.
 *
 * The v1.2 grid-template-columns rule above defines the visual track
 * order; each cell already carries an explicit `order:` value so
 * DOM-order cells land in the correct sticky/scrollable position.
 *
 * v1.1 keeps its base behavior (overflow-x: hidden, no sticky
 * columns beyond Action) unchanged. */

body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .table-scroll {
  overflow-x: auto;
  overflow-y: visible;
}

body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .table {
  --col-name-track: minmax(320px, 1fr);
}

/* Force the row to be at least as wide as the sum of its column
 * tracks + row padding. When the container is narrower than this,
 * .table-scroll grows a horizontal scrollbar and the sticky columns
 * pin. When the container is wider, `width: 100%` (from the base
 * .row rule) takes over and Name's 1fr absorbs the slack. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .table__head,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row {
  min-width: calc(
    var(--col-status) + var(--col-rcid) + 320px +
    var(--col-market) + var(--col-buyer) + var(--col-saleshub) +
    var(--col-updated) + var(--col-ver) + var(--col-action) + 32px
  );
}

/* Sticky-left cluster: Status, Rate Card ID, Name.
 *
 * Cumulative left offsets:
 *   Status       left: 0
 *   Rate Card ID left: var(--col-status)                  (e.g. 96)
 *   Name         left: calc(--col-status + --col-rcid)    (e.g. 296)
 *
 * Row padding (16px left) is naturally absorbed during sticky pinning:
 * at scrollLeft=0 the cells sit at their natural grid positions
 * (Status at 16, Rate Card ID at 112, Name at 312). As scrollLeft
 * grows past 16, sticky pins each cell to its declared offset (0, 96,
 * 296) with no visual seam because the sticky cells' opaque
 * background covers the row's padding region.
 *
 * All three sticky-left cells share z-index:3 with the sticky-right
 * Action column so their combined stacking context always wins over
 * non-sticky cells (which paint at default z-index 0). */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .table__head .th--status,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row .cell--status {
  position: sticky;
  left: 0;
  background: var(--surface);
  z-index: 3;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .table__head .th--rate-card-id,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row .cell--rate-card-id {
  position: sticky;
  left: var(--col-status);
  background: var(--surface);
  z-index: 3;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .table__head .th--name,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row .name {
  position: sticky;
  left: calc(var(--col-status) + var(--col-rcid));
  background: var(--surface);
  z-index: 3;
}
/* Header sticky cells fill the 32px head height so the row divider
 * underline stays aligned. Body cells inherit .row's 48px height. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .table__head .th--status,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .table__head .th--rate-card-id,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .table__head .th--name {
  height: 100%;
}

/* Right-edge shadow on the sticky Name column, revealed only while
 * something is actually scrolling behind it (.is-name-stuck toggled
 * by initStickyNameShadow). Mirrors the sticky-Action shadow on the
 * opposite side. Base .table-scroll rules keep the shadow rule for
 * v1.1 inert (no sticky Name there). */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .table-scroll.is-name-stuck .table__head .th--name,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .table-scroll.is-name-stuck .row .name {
  box-shadow: 8px 0 8px -6px rgba(15, 18, 20, 0.12);
}

/* Row hover / selected: compose the tint on top of the sticky cell's
 * opaque base so scrolled content stays hidden while the visible row
 * picks up its full chrome. Mirrors the existing Action-sticky
 * composition pattern (see .theme-ads .row:hover .actions).
 *
 * Selected state uses the opaque --ads-brand-wash directly (no
 * gradient composite needed). */
body.theme-ads:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row:hover .cell--status,
body.theme-ads:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row:hover .cell--rate-card-id,
body.theme-ads:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row:hover .name {
  background:
    linear-gradient(var(--ads-surface-hover), var(--ads-surface-hover)),
    var(--surface);
}
body.theme-ads:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row.is-selected .cell--status,
body.theme-ads:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row.is-selected .cell--rate-card-id,
body.theme-ads:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row.is-selected .name {
  background: var(--ads-brand-wash);
}

.theme-ads .table__body .row:focus-visible {
  outline: var(--ads-input-border-active) solid var(--ads-brand);
  outline-offset: calc(-1 * var(--ads-input-border-active));
}

/* Resizer stacking: handles between two sticky columns need to
 * paint above the right neighbor's sticky stacking context. Sticky
 * cells sit at z-index:3; their resizers (position:absolute inside
 * the header cell) inherit z-index:1 relative to their own sticky
 * stacking context, which puts them BELOW the adjacent sticky
 * column at z-index:3. Bumping the resizer to z-index:4 lifts the
 * 8px hit area above the neighbor so drag still lands.
 *
 * In v1.2 visual order (Status, Rate Card ID, Name, Marketplace,
 * Buying Entity, SalesHub, Updated, Ver, Action) the resizers that
 * straddle a sticky right-neighbor are:
 *   Status resizer      -> straddles Rate Card ID (sticky)
 *   Rate Card ID resizer-> straddles Name (sticky)
 *   Ver resizer         -> straddles Action (sticky)
 * Marketplace's resizer already has z-index:4 for the same reason
 * (existing 2026-07-13 fix); the Name resizer straddles Marketplace
 * (non-sticky) so it stays at z-index:1. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .th--status .th-resizer,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .th--rate-card-id .th-resizer,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .th--ver .th-resizer {
  z-index: 4;
}

/* Narrow-viewport tightening for the sticky cluster (v1.2 only).
 *
 * With base widths (Status 96, Rate Card ID 200, Name 320, Action
 * 170) the sticky-left cluster ends at container-x 616 and sticky
 * Action pins at container-x = container-width - row-padding-right
 * - 170 = container-width - 186. The two sticky regions collide
 * when container width falls below 616 + 186 = 802px. Accounting
 * for the vnav (64), page__content padding (16+16 at <=1023) and
 * the ~15px browser vertical scrollbar reservation, that means
 * viewport widths at or below 912px push the two sticky regions
 * into overlap.
 *
 * The brief calls out "Sticky columns do not overlap" as a
 * verification step. Tightening the four sticky columns at
 * viewport widths <=999 pulls the cluster end down to 476 and
 * sticky Action width down to 160 (tighter than 170 base), so
 * the total sticky reserve is 476 + 16 + 160 = 652 with 16px
 * left over for the row padding on the right edge. At the
 * minimum supported viewport (768) the container is ~672px wide,
 * leaving comfortable positive gap between the two sticky
 * regions (no overlap).
 *
 * Column values chosen:
 *   Status  88px  - fits the "Published" chip (~70px) plus 8/8 pad.
 *   Rate Card ID 168px - most IDs fit; long ones truncate cleanly
 *                       via the data-tooltip-truncate="auto" tooltip.
 *   Name floor  220px  - wider than the shortest recognisable rate
 *                       card names ("WPP Upfront...", "IPG
 *                       L'Oreal..." etc.) at 14/21 Open Sans; longer
 *                       names truncate with ellipsis + tooltip.
 *   Action  160px - 5 icons x 24 + 4 gaps x 8 + 8 left pad = 160.
 *
 * `!important` is required because initColumnResize (app.js) writes
 * user-dragged widths as inline styles on .table; without it a
 * previously-dragged Status width would carry over into narrow
 * viewports and re-introduce the overlap. Inline styles beat
 * regular stylesheet declarations at every specificity; !important
 * flips that so the responsive layout always wins. */
@media (max-width: 999px) {
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .table {
    --col-status:     88px  !important;
    --col-rcid:      168px  !important;
    --col-action:    160px  !important;
    --col-name-track: minmax(220px, 1fr) !important;
  }
  /* Update the min-width sum to use the tightened Name floor so
   * the row's intrinsic width tracks the new sticky reserve. The
   * media query above already tightened --col-status / --col-rcid
   * / --col-action; the only literal that needs matching is the
   * Name floor. */
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .table__head,
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row {
    min-width: calc(
      var(--col-status) + var(--col-rcid) + 220px +
      var(--col-market) + var(--col-buyer) + var(--col-saleshub) +
      var(--col-updated) + var(--col-ver) + var(--col-action) + 32px
    );
  }
}

/* Compact viewports cannot fit all three sticky identifiers plus the
 * sticky Action column. Keep Status and Action pinned, and let Rate Card ID
 * and Name participate in horizontal scrolling until tablet width returns. */
@media (max-width: 699px) {
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .table__head .th--rate-card-id,
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row .cell--rate-card-id,
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .table__head .th--name,
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row .name {
    position: relative;
    left: auto;
    z-index: 0;
  }
}

body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row .actions {
  flex-wrap: nowrap;
  white-space: nowrap;
}

/* ---- Marketplace resizer stacking (kept for defense-in-depth) ------ *
 * Originally introduced 2026-07-13 to lift Marketplace's resizer
 * above the sticky Action column at the (now removed) ≤1140 breakpoint
 * where responsive column-hiding pushed Marketplace next to Action.
 * The 2026-07-29 horizontal-scroll refactor removed the column-hiding
 * rules and Marketplace's right neighbor is once again Buying Entity
 * (non-sticky) at every v1.2 viewport. The bump to z-index:4 is kept
 * as belt-and-suspenders so any future layout that re-introduces a
 * sticky right neighbor won't reintroduce the drag-to-resize bug. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .th--marketplace .th-resizer {
  z-index: 4;
}

/* ---- Name header resizer clip fix, v1.2 only (2026-07-13) --------- *
 * Bug: the resize handle between Name and Marketplace can't be
 * dragged.
 *
 * Root cause: .th--name is the sole flex-and-shrink header column
 * (minmax(0, 1fr)) and carries `overflow: hidden` so long labels
 * clip at the box edge (see the "Name column (flex-and-shrink)"
 * block above). The .th-resizer is an absolute child of .th--name
 * positioned at `right: -4px; width: 8px` so its 8px hit area
 * STRADDLES the column boundary. `overflow: hidden` clips both the
 * paint AND the pointer-events for the 4px portion of the resizer
 * that overhangs .th--name's right edge, leaving at most a narrow
 * 4px strip inside Name as the effective hit area (browsers vary
 * on whether the strip receives events at all when the child is
 * absolute-positioned inside an overflow-hidden inline-flex button).
 *
 * Fix: swap `overflow: hidden` to `overflow: clip` with an
 * overflow-clip-margin large enough to let the resizer's 4px
 * overhang escape the clip. Text truncation is unaffected because
 * the clip still occurs at the box edge (the header label "Name"
 * plus the .th__sort glyph clip identically to before). Body Name
 * cells (`.row .name`) keep `overflow: hidden` intact so the
 * `.name__link` ellipsis behaves the same way. Every other
 * resize handle is unaffected because Name is the only header
 * with an overflow-hidden ancestor.
 *
 * Scope: v1.2 only per the brief. v1.0 / v1.1 keep the pre-
 * existing overflow: hidden even though the same bug exists there;
 * "do not modify v1.1" trumps the shared-root-cause instinct.
 *
 * Compatibility: `overflow: clip` + `overflow-clip-margin` are
 * baseline widely available (Chrome 90+, Firefox 90+, Safari 16.4+,
 * Edge 90+), which covers every browser the deck targets.
 *
 * The resizer already has `z-index: 1` from its base rule; nothing
 * in the header stacking context sits above it inside .th--name
 * (the label text node and .th__sort span are static-flow, so
 * they're below z-index:1 by default). The visible ::after
 * divider paints via pointer-events:none, so even the hover glow
 * cannot swallow the drag. The 8px hit area now stays live
 * end-to-end. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .table__head .th--name {
  overflow: clip;
  overflow-clip-margin: 8px;
}

/* ---- Name link click hardening, v1.2 only (2026-07-13) ----------- *
 * Brief: guarantee the rate-card Name link always receives pointer
 * events so the link opens Edit Rate Card, regardless of any
 * decorative overlay, fade mask, gradient wash, sibling pseudo-
 * element, or future truncation treatment that might land inside
 * .row .name.
 *
 * Baseline behavior (verified via headless-Chrome probe at 1440px):
 * .name__link is reachable via elementFromPoint at the link's
 * center, and calling .click() navigates from list -> create with
 * the correct ?section=create&mode=edit&cardId=... query. The
 * ancestor chain contains no overlay:
 *   A.name__link > DIV.name > DIV.row > DIV.table__body > ...
 * There is no current overlay in the cell, but the brief asks for a
 * defensive hardening pass to lock in this behavior.
 *
 * Hardening layers:
 *   1. .row .name becomes a positioning root. The 2026-07-29
 *      horizontal-scroll refactor promoted this from
 *      `position: relative` to `position: sticky` (see the "Sticky-
 *      left cluster" block above) so the Name column can pin to the
 *      left of .table-scroll during horizontal scroll while STILL
 *      serving as a positioning root for its .name__link child. The
 *      z-index layering rules below work identically under sticky
 *      because `position: sticky` establishes both a stacking
 *      context and a positioning root for absolute-positioned
 *      descendants. This rule is retained (and no longer sets
 *      `position:`) so the layering guarantee is explicit and
 *      version-scoped even if someone edits the sticky block later.
 *   2. .name__link gets position: relative + z-index: 1 +
 *      pointer-events: auto so it always paints and hit-tests above
 *      any sibling at the default z-index tier. isolation:isolate
 *      guarantees a fresh stacking context so no ancestor gradient
 *      can raise its own z-index above the anchor.
 *   3. Every non-anchor descendant of .row .name (present or future
 *      decorative element) and both pseudo-elements are forced to
 *      pointer-events:none. Clicks that land on decoration fall
 *      through to the underlying .row click handler (which already
 *      opens Edit Rate Card for clicks outside interactive
 *      descendants). If someone later adds a fade-mask ::after or
 *      an absolute overlay <span>, they inherit pointer-events:none
 *      by construction.
 *
 * Keyboard interaction (Tab + Enter) is unaffected: <a href> stays
 * naturally focusable, focus-visible outline lives on .name__link
 * (see :focus-visible rule ~line 1418), Enter activates the
 * anchor's href which the click handler intercepts via
 * preventDefault + openEditRateCard.
 *
 * Column width, spacing, typography, colors, row height, and
 * Marketplace column are all untouched - this rule only adds
 * layering + pointer-events. v1.0 / v1.1 are not scoped here. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row .name__link {
  position: relative;
  z-index: 1;
  pointer-events: auto;
  isolation: isolate;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row .name > *:not(.name__link),
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row .name::before,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .row .name::after {
  pointer-events: none;
}

/* ---- LINE form Line-conditions row visibility, v1.2 only (2026-07-13) *
 * Adam's 2026-07-09 update consolidates the LINE form's three legacy
 * Line condition slots (Line condition 1 / 2 / 3) into a single
 * comma-separated "Line conditions" field. The HTML now carries BOTH
 * variants side by side (data-lc-version="1.1" and data-lc-version="1.2")
 * so v1.1's demo state stays byte-identical while v1.2 shows the
 * consolidated field. This rule hides the wrong variant per active
 * version.
 *
 * v1.0 and v1.1 fall through the default branch (::not selector)
 * so the v1.2 row stays hidden and the v1.1 row renders. Only v1.2
 * flips: the v1.1 row is hidden and the v1.2 row is shown. Same
 * pattern is used in the Quick Edit thead + colgroup swap in
 * renderQuickEditRows / renderQuickEditHeader (see app.js). */
[data-lc-version="1.2"] {
  display: none;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) [data-lc-version="1.1"] {
  display: none;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) [data-lc-version="1.2"] {
  display: grid;                        /* restore .create-form__row--three grid */
}

/* ---- Rate card name truncation ellipsis, v1.2 only (2026-07-16) --
 *
 * Brief: at narrower viewports the Rate card name input can no
 * longer show the full value; the user sees the value clipped
 * mid-word with no visual affordance that more text exists off to
 * the right. Two touchpoints where this happens:
 *
 *   1. #rc-name              on the Create / Edit form (editable)
 *   2. [data-rcle-field="name"] on the RCLE overlay      (read-only)
 *
 * Fix pair (CSS + JS):
 *
 *   - CSS (this rule): text-overflow: ellipsis on both inputs so
 *     the browser paints ... at the end of an overflowing value
 *     while the input is unfocused. Modern <input type="text">
 *     honors text-overflow: ellipsis in Chrome/Safari/Firefox/Edge
 *     when the value exceeds the visible width. Focus reverts to
 *     the native scroll-with-caret behavior automatically.
 *
 *   - HTML (index.html): data-tooltip-from-value="true" plus
 *     data-tooltip-truncate="auto" opt each input into the shared
 *     ADS tooltip runtime (adsTooltipShow in app.js). The runtime
 *     reads the current .value on show + gates on scrollWidth >
 *     clientWidth so the tooltip only appears when the value is
 *     genuinely truncated.
 *
 * Preservation:
 *   - Field width, height, label, border, spacing, validation, and
 *     editing behavior are untouched. text-overflow only changes
 *     how overflowing text is PAINTED; the value itself is
 *     unchanged and remains fully editable.
 *   - Three-column form layout untouched (no width changes).
 *   - No wrap, no row-height change, no accordion / panel change.
 *
 * Scope: v1.2 only per the prior-task convention. v1.0 / v1.1
 * keep the earlier browser-default clip behavior so their demo
 * state stays byte-identical. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) #rc-name,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) [data-rcle-field="name"] {
  text-overflow: ellipsis;
}

/* ---- Edit / Create header vertical rhythm, v1.2 only (2026-07-16) *
 * Brief: at narrow desktop widths (<=899, when .create__header wraps
 * the Save-as-draft / Save-changes action cluster onto its own row)
 * the gap between the subtitle and the buttons becomes ~65px total,
 * which reads as an empty band that pushes the Rate Card details
 * card down the page. The user brief calls for a compact ~16px
 * vertical gap once the actions wrap, matching the intentional
 * spacing of the wide-desktop layout.
 *
 * Root cause: three additive rules combine at <=899:
 *
 *   1. .create__header { gap: 24px } (desktop, line ~3377) stays
 *      applied when the flex parent wraps, giving a 24px row-gap
 *      between the .page__heading row and the .create__actions row.
 *
 *   2. .page__actions { margin-top: 12px } inside @media <=899
 *      (line ~5111) adds 12px on top. Margins do NOT collapse into
 *      flex row-gap, so the total between subtitle bottom and
 *      button top becomes 24 + 12 = 36px.
 *
 *   3. .page__header { padding-bottom: 28px } inside @media <=899
 *      (line ~5105) was written for the Rate Card Manager list
 *      page (where the header sits above the search toolbar and
 *      needs breathing room). On the Create / Edit page the
 *      .page__content already owns the gutter that separates the
 *      header from the first card, so this 28px is dead vertical
 *      space that pushes the CARD accordion 28px farther down.
 *
 * Fix (v1.2 only, [data-page="create"] only, <=899 only):
 *
 *   - row-gap: 16px on .create__header replaces the inherited 24px
 *     with the brief-mandated 16px. column-gap stays 24px so the
 *     wide layout (no wrap) still respects the horizontal breathing
 *     room between the .page__heading and the .create__actions.
 *
 *   - margin-top: 0 on .create__actions removes the 12px addition
 *     so the total subtitle -> buttons gap becomes exactly the
 *     16px row-gap. Buttons themselves keep their gap: 12px between
 *     each other (from .create__actions gap in the desktop rule),
 *     which lands in the brief's 8-12px range.
 *
 *   - padding-bottom: 0 on .page__header removes the list-page
 *     breathing-room padding for the create/edit page. The card
 *     accordion below now sits at the same distance below the
 *     header as it does at wide widths (defined by .create__topwrap
 *     padding-bottom + .page__content padding-top).
 *
 * Preservation checklist per brief:
 *   - Wide desktop layout: untouched. Rule fires only at <=899.
 *   - Back link, title, subtitle, button labels, button styling,
 *     button behavior, disabled states: untouched.
 *   - Actions stay LEFT-aligned once wrapped: preserved.
 *     .create__actions keeps width: 100% (line 4494); its child
 *     buttons flow left-to-right with default flex alignment (no
 *     justify-content: space-between introduced).
 *   - Two actions on same row when space allows: preserved.
 *     Inner button gap stays at 12px.
 *   - No fixed height, no min-height, no negative margin.
 *   - Save as draft + Save changes stay in .create__actions,
 *     they do NOT move into the blue top nav. */
@media (max-width: 899px) {
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) [data-page="create"] .create__header {
    row-gap: 16px;
  }
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) [data-page="create"] .create__actions {
    margin-top: 0;
  }
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) [data-page="create"] .page__header {
    padding-bottom: 0;
  }
}

/* ---- (4) Border radius canonicalization, v1.2 only (2026-07-09) ---- *
 * Fidel specified 12px as the canonical corner radius for every
 * non-circular rounded surface in v1.2 (accordions, cards, table,
 * inputs, dropdowns, buttons, pagination, modals, popovers, toasts,
 * sidebar rows, and explainer-page inner cards). This block scopes
 * every override to body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) so v1.0 and v1.1 render
 * byte-for-byte identically to before this commit.
 *
 * Two-pronged approach:
 *   1. Flip the ADS design tokens (--ads-radius-sm / --ads-input-
 *      radius / --ads-popover-radius / --ads-toast-radius / --vnav-
 *      row-radius) from 6px to 12px at v1.2 scope. Every component
 *      that consumes a token picks up 12px automatically.
 *   2. Override the remaining hardcoded values (8/14/16/18) on the
 *      specific classes that render in v1.2 UI. Selectors carry a
 *      body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) prefix so v1.1 specificity is
 *      strictly less and v1.1 wins there.
 *
 * Explicitly preserved (intentionally NOT 12px, matches user brief):
 *   - Fully circular / pill surfaces (avatar bell, apps, avatar
 *     badge, status chips, search input, ADS radius-pill token,
 *     radio / checkbox / toggle indicators). The user brief said
 *     "if a component must remain fully circular for functional
 *     reasons ... do not force those to 12px" and "do not change
 *     pill/chip shape if the ADS component is intentionally pill-
 *     shaped".
 *   - Date-picker day cells (3px rest / 6px selected via --ads-
 *     radius-day / --ads-radius-day-active tokens) — micro-cells
 *     inside the popover, not major surfaces.
 *   - Focus outlines (2-4px on .th--sortable, .icon-btn, day cells,
 *     scrollbar-thumb) — a11y chrome, not a rounded surface.
 *   - Atlas presentation deck outer frame (.atlas-stage and .atlas-
 *     slide--connected-workflows at 40px) — the 1920x1080 outer
 *     viewport of the slide deck, functionally a presentation
 *     surround not a UI card. Flagged for review in QA report.
 * ================================================================= */

/* ============================================================================
 *  v1.2 border-radius: CORRECTED (2026-07-13)
 *  ---------------------------------------------------------------------------
 *  Original 12px sweep flipped small-radius tokens (input, button, popover,
 *  vnav) to 12px, which over-rounded every interactive control against ADS
 *  spec. Per the ADS Figma:
 *    Button default (17:542) - 6px  (--button/size/default/radius,6px)
 *    Button compact          - 3px
 *    Input  default (63:216) - 6px  (--input/size/default/radius,6px)
 *    Input  compact          - 3px
 *    Search               - 100px pill (--search/size/radius,100px)
 *    Dropdown / popover      - 6px
 *    Card / modal panel      - 12px (--ads-radius-md, Fidel's brief)
 *
 *  This block restores small controls to 6px while keeping card /
 *  modal / accordion / toast containers at 12px (per user brief:
 *  "Do not modify card-container radii" and "Do not modify toast").
 * ============================================================================ */

body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) {
  --ads-radius-sm:         6px;    /* ADS input + button radius     */
  --ads-input-radius:      6px;    /* ADS input default             */
  --ads-popover-radius:    6px;    /* ADS dropdown menu             */
  --ads-toast-radius:      12px;   /* card-like floating surface    */
  --vnav-row-radius:       6px;    /* sidebar row (interactive)     */
}

/* Card containers stay at 12px per Fidel's brief. Explicit selector
 * so the token change above does not accidentally regress cards
 * that consume --ads-radius-md via .surface / .create__card. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .surface,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .create__card,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]).theme-ads .create__card,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]).theme-ads .surface.create__card {
  border-radius: 12px;
}

body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) [data-page="list"] > .page__content > .surface {
  border-radius: var(--ads-radius-card);
}

/* Modal panels remain at 12px. .modal--ads .modal__panel already
 * lives at 12px via the ADS theme; this rule catches the legacy
 * .modal__panel (was 8px pre-standardization) and holds it at 12px. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .modal__panel {
  border-radius: 12px;
}

/* Atlas explainer inner cards. The base .wpc__card at 18px covers
 * all three tiles on the "Why Pricing Gets Complicated" slide
 * (Buyer, RCM, CP); the Buyer hover popover panel (.bcm__panel) at
 * 16px is the pop-open explanation. Both flip to 12px in v1.2.
 * Outer deck frame (.atlas-stage / .atlas-slide--connected-workflows
 * at 40px) is intentionally left as the presentation-viewport
 * surround. The decorative .wpc__window-strip (11.44px scaled from
 * the Figma frame) stays at its scaled value because it is a
 * browser-chrome mock inside the RCM/CP diagram, not a UI surface. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide--why-pricing .wpc__card,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide__cta-tip,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .bcm__panel {
  border-radius: 12px;
}

/* ============================================================================
 *  ATLAS INTRO DECK (2026-06-29 rebuild, 2026-08-04 re-order)
 *
 *  Standalone /atlas route renders the presentation deck. Every slide is
 *  authored as isolated HTML/CSS/SVG at the Figma-native 1920x1080
 *  coordinate system so it scales as one unit via CSS transform: scale().
 *
 *  The running order and each slide's Figma node live with the markup, in
 *  the ROUTE: atlas comment in index.html. That is the only copy; the one
 *  that used to sit here went stale every time a slide moved.
 *
 *  Layout (2-layer stacking model on .atlas-deck):
 *    z:auto  background           .atlas-deck (--presentation-canvas)
 *    z:1     slide stage          .atlas-stage-wrap > .atlas-stage > .atlas-slide
 *
 *  No nav chrome - click/tap anywhere on the stage advances; keyboard
 *  (Left/Right/Space/PageUp/PageDown/Home/End/Esc) is wired in
 *  initAtlasDeck() in app.js.
 *
 *  Scaling: the 1920x1080 stage scales as a single unit via
 *  transform: scale(--atlas-scale) computed by fitStage() in
 *  app.js > initAtlasDeck(). The stage is pointer-events: none; only
 *  the [data-atlas-enter] button on the final slide (9) opts back
 *  into pointer events for the CTA into the Rate Card Manager.
 * ========================================================================== */

/* When atlas is the active route, suppress every other chrome bit so
 * the deck reads as a true full-bleed presentation surface. */
body[data-route="atlas"] .gnav,
body[data-route="atlas"] .vnav,
body[data-route="atlas"] .filter-panel,
body[data-route="atlas"] .filter-overlay,
body[data-route="atlas"] .ads-tt,
body[data-route="atlas"] .toast,
body[data-route="atlas"] .dlsim-root,
body[data-route="atlas"] .dlsim-toast-host {
  display: none !important;
}
body[data-route="atlas"] .page-route[data-page="atlas"] {
  display: block !important;
}
/* ============================================================================
 * PRESENTATION CANVAS (shared)
 * The single surface every presentation surface sits on: the Atlas deck
 * and the Rate Card Layer Explainer overlay both read this token, so a
 * new slide inherits the canvas without declaring anything of its own.
 *
 * It is deliberately lighter than the slides themselves (#020024) so the
 * slide viewport's radius has something to be a corner against. Slide
 * interiors, cards, live product views and the normal product UI are all
 * out of scope for this token.
 * ========================================================================== */
:root {
  --presentation-canvas: #1E1E1E;
  --presentation-slide-surface: #020024;
  --presentation-slide-shadow: 0 12px 32px rgba(0, 0, 0, 0.22);

  /* Shell geometry. The Atlas deck and the Rate Card Layer Explainer are
   * two different surfaces, but a viewer moving between them is looking at
   * one presentation, so the canvas they sit on has to be the same object:
   * same breathing room to the window, same corner, same brand mark in the
   * same place. Both read these rather than each carrying its own copy,
   * which is how the explainer drifted to a full-bleed panel with the mark
   * pinned to the window instead of to the canvas. */
  --presentation-safe-inset: 24px;
  --presentation-slide-radius: 40px;
  /* Authored as 36x41 at 58/42 in the deck's own 1920x1080 canvas, then
   * expressed as a share of the canvas. The deck scales its stage as one
   * unit and the explainer canvas is fluid, so a pixel value would render
   * the mark at two different sizes on two surfaces that are meant to
   * look like the same presentation. A share of the canvas lands
   * identically on both. Height follows the mark's own ratio. */
  --presentation-brand-width: 1.875%;     /*  36 / 1920 */
  --presentation-brand-right: 3.0208%;    /*  58 / 1920 */
  --presentation-brand-bottom: 3.8889%;   /*  42 / 1080 */
}

body[data-route="atlas"] {
  background: var(--presentation-canvas);
  overflow: hidden;
  height: 100%;
  min-height: 100%;
}

/* Keep the presentation canvas painted even before the deck has, and
 * where the browser exposes html around a fixed child. */
html:has(body[data-route="atlas"]) {
  background: var(--presentation-canvas);
  height: 100%;
  overflow: hidden;
}

/* ---------- Deck container -------------------------------------------- */
.atlas-deck {
  position: fixed;
  inset: 0;
  background: var(--presentation-canvas);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  z-index: 100;
  isolation: isolate;

  /* ------------------------------------------------------------------ type
   * Semantic typography roles for the deck, resolved to the real Inspire
   * TWDC weights declared at the top of this file. Slide rules reference
   * these instead of choosing a weight each time, so a role can only
   * change in one place.
   *
   * Weights are the faces the family actually ships. There is no Bold and
   * no Semibold in Inspire TWDC, so "strongest" is Black 800 and the step
   * below it is Heavy 600.
   *
   *   --font-display     96/48/36  Black 800   cover lockup
   *   --font-heading     56        Heavy 600   slide titles
   *   --font-subheading  36        Heavy 600   slide subtitles
   *   --font-label       24-40     Black 800   in-diagram labels, callouts
   *   --font-body        22-32     Medium 500  supporting copy
   *   --font-caption     19-24     Roman 400   small print, footnotes
   *   --font-quiet       32        Light 300   de-emphasised detail
   */
  --font-family-inspire: "InspireTWDC", "MultiplaneTWDC Display", "Open Sans", sans-serif;
  --font-display: 600;   /* cover lockup renders Heavy, not Black */
  --font-heading: 600;
  --font-subheading: 600;
  --font-label: 800;
  --font-body: 500;
  --font-caption: 400;
  --font-quiet: 300;
}

/* The family ships five discrete weights and no italic outside Roman, so the
 * browser must never invent one. Without this a rule asking for 700 or 900
 * gets a synthetic smear of the nearest face, which is the exact defect this
 * pass removes. */
.atlas-deck,
.atlas-deck * {
  font-synthesis: none;
  -webkit-font-smoothing: antialiased;
}

/* ---------- Stage wrap (clipping + centering box for the scaled stage)
 *  Click anywhere here advances the deck (handler in initAtlasDeck).
 *  Cursor stays the platform default so the deck reads as a
 *  presentation surface, not a button. */
.atlas-stage-wrap {
  position: relative;
  flex: 1 1 auto;
  display: block;
  box-sizing: border-box;
  min-width: 0;
  min-height: 0;
  padding:
    max(var(--presentation-safe-inset), var(--app-safe-area-top))
    max(var(--presentation-safe-inset), var(--app-safe-area-right))
    max(var(--presentation-safe-inset), var(--app-safe-area-bottom))
    max(var(--presentation-safe-inset), var(--app-safe-area-left));
  overflow: hidden;
  z-index: 1;
  pointer-events: auto;
  cursor: default;
}

/* ---------- Native-resolution 1920x1080 stage -------------------------
 *  JS (fitStage) writes --atlas-scale and margin-left/top so the stage
 *  shrinks proportionally to fit the wrap with a 24px breathing inset
 *  on each axis. */
.atlas-stage {
  position: relative;
  width: 1920px;
  height: 1080px;
  background: var(--presentation-slide-surface);
  border-radius: var(--presentation-slide-radius);
  overflow: hidden;
  transform-origin: top left;
  transform: scale(var(--atlas-scale, 1));
  box-shadow: var(--presentation-slide-shadow);
  pointer-events: none;
}

/* ---------- Slides (one per Figma frame) ------------------------------
 *  Each .atlas-slide fills the 1920x1080 stage. Only the .is-active
 *  slide is rendered (display: block; the rest stay display: none so
 *  the inactive SVGs do NOT remain in the layer tree consuming GPU
 *  composition). */
/* Slides carry the deck's navy themselves rather than letting the canvas
 * show through. Several slides only ever painted it on an inner wrapper,
 * which happened to look right while the canvas was near-black; a slide
 * that forgets it should not be able to leak the canvas. */
.atlas-slide {
  position: absolute;
  inset: 0;
  display: none;
  background: var(--presentation-slide-surface);
}
.atlas-slide.is-active { display: block; }

/* ---------- Shell brand mark ------------------------------------------
 * Six slides carry the Disney Advertising mark in the canvas corner, and
 * each used to repeat the same four coordinates. One rule now places all
 * of them, from the same tokens the explainer reads, so the mark cannot
 * drift by a pixel between one surface and the next. Slides that show a
 * product logo of their own inside the artwork are content, not shell,
 * and are deliberately left alone. */
.atlas-slide .rpx__logo,
.atlas-slide .sup__logo,
.atlas-slide .usc__logo,
.atlas-slide .sar__logo,
.atlas-slide .mpl__logo,
.atlas-slide .clo__logo {
  position: absolute;
  right: var(--presentation-brand-right);
  bottom: var(--presentation-brand-bottom);
  left: auto;
  top: auto;
  width: var(--presentation-brand-width);
  height: auto;
  display: block;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* The SVG fills its slide exactly 1:1 - it IS the slide content. */
.atlas-slide__svg {
  display: block;
  width: 1920px;
  height: 1080px;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* ============================================================================
 * SLIDE 1: Cover
 * Figma node 492:28709, authored at the deck's native 1920 x 1080 size.
 * The radial navy field is carried as an SVG so the five-stop gradient
 * keeps Figma's transform exactly; CSS radial-gradient cannot express it.
 * ========================================================================== */
.atlas-slide--cover {
  --cov-text: #FFFFFF;
  overflow: hidden;
  border-radius: 40px;
  background:
    url("./assets/atlas/cover-background.svg") 0 0 / 100% 100% no-repeat;
}

.atlas-slide--cover .cov {
  position: absolute;
  inset: 0;
  overflow: hidden;
  color: var(--cov-text);
  font-family: "InspireTWDC", "MultiplaneTWDC Display", "Open Sans", sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: geometricPrecision;
}

.atlas-slide--cover .cov__lockup {
  position: absolute;
  top: 454px;
  left: 160px;
  display: block;
  width: 362px;
  height: 38px;
  max-width: none;
  object-fit: cover;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* Figma centers each text box on its own fixed-height frame, so the
 * boxes are laid out by their top edge and the glyphs are centered
 * inside them rather than sitting on the baseline. */
.atlas-slide--cover .cov__title,
.atlas-slide--cover .cov__presenter,
.atlas-slide--cover .cov__date {
  position: absolute;
  left: 160px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  margin: 0;
  color: var(--cov-text);
  font-family: var(--font-family-inspire);
  font-style: normal;
  font-weight: var(--font-display); /* InspireTWDC Heavy 600 */
  line-height: 1.2;
  white-space: nowrap;
}

.atlas-slide--cover .cov__title {
  top: 518px;
  width: 1780px;
  height: 146px;
  font-size: 96px;
}

.atlas-slide--cover .cov__presenter {
  top: 664px;
  width: 301px;
  height: 58px;
  font-size: 48px;
}

.atlas-slide--cover .cov__date {
  top: 722px;
  width: 186px;
  height: 76px;
  font-size: 36px;
  opacity: 0.3;
}

/* ============================================================================
 * SLIDE 2: A Rate Card tells us the right price for each deal
 * Figma node 736:248, authored at the deck's native 1920 x 1080 size.
 *
 * The slide reads as one horizontal formula:
 *   Buyer + Deal + Product + Price Change = Right Price
 * Each term is a card plus the question underneath it, and the two move
 * as a single unit so the question is never orphaned from its card.
 *
 * Reveal: every animated part is laid out at its final position from the
 * first frame and only opacity / transform change, so nothing on the
 * slide can shift while the sequence plays. initAtlasRightPrice() in
 * app.js owns the ordering and the timings; this file owns how each
 * step looks. The gold result card is held back behind
 * [data-rpx-phase="result-visible"] rather than a class on the card, so
 * a half-finished run can never leave it showing.
 * ========================================================================== */
.atlas-slide--right-price {
  --rpx-background: #020024;
  --rpx-title: #FFFFFF;
  --rpx-card-surface: #202735;
  --rpx-card-border: #384252;
  --rpx-card-text: #F1F2F7;
  --rpx-result-border: #BD975E;
  --rpx-result-glow: rgba(189, 151, 94, 0.34);
  /* Reveal contract shared with app.js and qa_right_price_slide.py. */
  --rpx-term-duration: 460ms;
  --rpx-operator-duration: 240ms;
  --rpx-result-duration: 540ms;
  --rpx-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  overflow: hidden;
  border-radius: 40px;
  background: var(--rpx-background);
}

.atlas-slide--right-price .rpx {
  position: absolute;
  inset: 0;
  overflow: hidden;
  color: var(--rpx-title);
  font-family: "InspireTWDC", "MultiplaneTWDC Display", "Open Sans", sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: geometricPrecision;
}

/* ---------- Header ----------------------------------------------------- */
.atlas-slide--right-price .rpx__header {
  position: absolute;
  top: 88px;
  left: 63px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 1828px;
  gap: 8px;
}

.atlas-slide--right-price .rpx__title,
.atlas-slide--right-price .rpx__subtitle,
.atlas-slide--right-price .rpx__card-label,
.atlas-slide--right-price .rpx__question {
  margin: 0;
  font-style: normal;
}

.atlas-slide--right-price .rpx__title {
  width: 1727px;
  color: var(--rpx-title);
  font-family: var(--font-family-inspire);
  font-size: 56px;
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
  line-height: 56px;
}

.atlas-slide--right-price .rpx__subtitle {
  width: 1808px;
  color: var(--rpx-title);
  font-family: var(--font-family-inspire);
  font-size: 36px;
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
  line-height: 56px;
}

/* ---------- Formula terms (card + its question) ------------------------ */
.atlas-slide--right-price .rpx__group {
  position: absolute;
  top: 392px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 31px;
}

.atlas-slide--right-price .rpx__group--buyer        { left: 63px;   width: 230.4px; }
.atlas-slide--right-price .rpx__group--deal         { left: 384px;  width: 230.4px; }
.atlas-slide--right-price .rpx__group--product      { left: 700px;  width: 241px; }
.atlas-slide--right-price .rpx__group--price-change { left: 1026px; width: 261px; }
.atlas-slide--right-price .rpx__group--result       { left: 1391px; width: 230.4px; }

.atlas-slide--right-price .rpx__card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 230.4px;
  height: 192px;
  gap: 9.6px;
  padding: 19.2px 57.6px;
  border: 1.2px solid var(--rpx-card-border);
  border-radius: 19.2px;
  background: var(--rpx-card-surface);
  box-shadow:
    0 12px 16.8px rgba(0, 0, 0, 0.32),
    inset 0 1.2px 14.4px rgba(255, 255, 255, 0.08);
}

.atlas-slide--right-price .rpx__card--result {
  border-width: 1.5px;
  border-color: var(--rpx-result-border);
}

.atlas-slide--right-price .rpx__icon {
  display: block;
  flex: 0 0 86.4px;
  width: 86.4px;
  height: 86.4px;
}

.atlas-slide--right-price .rpx__icon img,
.atlas-slide--right-price .rpx__operator img,
.atlas-slide--right-price .rpx__logo {
  max-width: none;
}

.atlas-slide--right-price .rpx__card-label {
  width: 192px;
  color: var(--rpx-card-text);
  font-family: var(--font-family-inspire);
  font-size: 28.8px;
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  line-height: normal;
  text-align: center;
  white-space: nowrap;
}

/* Figma node 736:396 sets this one label a fraction smaller than its
 * siblings. Kept so the slide matches the frame it was authored from. */
.atlas-slide--right-price .rpx__group--product .rpx__card-label {
  font-size: 28px;
}

.atlas-slide--right-price .rpx__question {
  width: 100%;
  color: var(--rpx-title);
  font-family: var(--font-family-inspire);
  font-size: 24px;
  font-weight: var(--font-body); /* InspireTWDC Medium 500 */
  line-height: 24px;
  text-align: center;
}

/* The two longest questions are set wider than their card and hang left
 * in the Figma frame rather than centring under it. */
.atlas-slide--right-price .rpx__group--product .rpx__question,
.atlas-slide--right-price .rpx__group--price-change .rpx__question {
  text-align: left;
}

/* ---------- Operators -------------------------------------------------- */
.atlas-slide--right-price .rpx__operator {
  position: absolute;
  display: block;
}

.atlas-slide--right-price .rpx__operator--plus-1 { top: 471px; left: 314px;  width: 50px; height: 50px; }
.atlas-slide--right-price .rpx__operator--plus-2 { top: 471px; left: 635px;  width: 45px; height: 45px; }
.atlas-slide--right-price .rpx__operator--plus-3 { top: 471px; left: 961px;  width: 45px; height: 45px; }
.atlas-slide--right-price .rpx__operator--equals { top: 470px; left: 1307px; width: 45px; height: 45px; }

.atlas-slide--right-price .rpx__operator img {
  width: 100%;
  height: 100%;
}


.atlas-slide--right-price .rpx__sr {
  position: absolute;
  top: 0;
  left: 0;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------- Reveal states ----------------------------------------------
 * Terms and operators sit at their final coordinates from the first
 * paint; only opacity and transform move, so the formula never reflows
 * while it assembles. */
.atlas-slide--right-price [data-rpx-reveal] {
  opacity: 0;
}

.atlas-slide--right-price .rpx__group[data-rpx-reveal] {
  transform: translateY(10px);
  transition:
    opacity var(--rpx-term-duration) var(--rpx-ease),
    transform var(--rpx-term-duration) var(--rpx-ease);
}

.atlas-slide--right-price .rpx__operator[data-rpx-reveal] {
  transition: opacity var(--rpx-operator-duration) var(--rpx-ease);
}

.atlas-slide--right-price [data-rpx-reveal].is-revealed {
  opacity: 1;
  transform: none;
}

.atlas-slide--right-price .rpx__group--result {
  opacity: 0;
  transform: scale(0.96);
  transform-origin: center center;
  transition:
    opacity var(--rpx-result-duration) var(--rpx-ease),
    transform var(--rpx-result-duration) var(--rpx-ease);
}

.atlas-slide--right-price .rpx[data-rpx-phase="result-visible"] .rpx__group--result {
  opacity: 1;
  transform: none;
}

/* One pass of gold, settling back to the card's resting shadow. */
.atlas-slide--right-price
  .rpx[data-rpx-phase="result-visible"]
  .rpx__card--result {
  animation: rpx-result-emphasis 900ms var(--rpx-ease) 1 both;
}

@keyframes rpx-result-emphasis {
  0% {
    box-shadow:
      0 12px 16.8px rgba(0, 0, 0, 0.32),
      inset 0 1.2px 14.4px rgba(255, 255, 255, 0.08),
      0 0 0 0 rgba(189, 151, 94, 0);
  }
  45% {
    box-shadow:
      0 12px 16.8px rgba(0, 0, 0, 0.32),
      inset 0 1.2px 14.4px rgba(255, 255, 255, 0.08),
      0 0 28px 6px var(--rpx-result-glow);
  }
  100% {
    box-shadow:
      0 12px 16.8px rgba(0, 0, 0, 0.32),
      inset 0 1.2px 14.4px rgba(255, 255, 255, 0.08),
      0 0 0 0 rgba(189, 151, 94, 0);
  }
}

/* Reduced motion keeps the staging, because the order the terms arrive in
 * is the explanation. Only the travel and the glow are dropped. */
@media (prefers-reduced-motion: reduce) {
  .atlas-slide--right-price .rpx__group[data-rpx-reveal] {
    transform: none;
    transition: opacity 120ms linear;
  }

  .atlas-slide--right-price .rpx__operator[data-rpx-reveal] {
    transition: opacity 120ms linear;
  }

  .atlas-slide--right-price .rpx__group--result {
    transform: none;
    transition: opacity 120ms linear;
  }

  .atlas-slide--right-price
    .rpx[data-rpx-phase="result-visible"]
    .rpx__card--result {
    animation: none;
  }
}

/* ============================================================================
 * SLIDE 8: RCM organizes pricing into three connected sections
 * Figma node 512:1580, authored at the deck's native 1920 x 1080 size.
 *
 * Replaces the retired "three building blocks" slide, which made the same
 * point as a static diagram. Three inputs stack down the left, each one a
 * card plus the question it answers, and the answer sits to the right
 * behind an equals sign.
 *
 * The equals sign and the Right Price card are the presenter's, not the
 * timer's: the automatic run stops after Premiums so the room can hear
 * what the three inputs are before the answer is on screen.
 *
 * Every visible size here is at least 30px so it still clears 16px once
 * fitStage() scales the 1920 canvas down to a 1280-wide window.
 * ========================================================================== */
.atlas-slide--three-questions {
  --tq-title: #F5F5F7;
  --tq-subtitle: #D2D2D7;
  --tq-card-label: #F5F7FA;
  --tq-accent: #2997FF;
  --tq-result-border: #BD975E;
  --tq-result-surface: #202735;
  --tq-result-label: #F1F2F7;
  /* Reveal contract shared with app.js and qa_three_questions_slide.py. */
  --tq-header-duration: 500ms;
  --tq-group-duration: 500ms;
  --tq-mark-duration: 250ms;
  --tq-equals-duration: 340ms;
  --tq-result-duration: 500ms;
  --tq-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  overflow: hidden;
  border-radius: 40px;
  background: #020024;
}

.atlas-slide--three-questions .tq {
  position: absolute;
  inset: 0;
  overflow: hidden;
  color: #FFFFFF;
  /* The slide's own base, so nothing here inherits the app's 14px body
   * size. Every explicit size below is >= 30px. */
  font-size: 30px;
  font-family: "InspireTWDC", "MultiplaneTWDC Display", "Open Sans", sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: geometricPrecision;
}

/* ---------- Header ------------------------------------------------------- */
.atlas-slide--three-questions .tq__header {
  position: absolute;
  top: 88px;
  left: 63px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 1828px;
  gap: 8px;
}

.atlas-slide--three-questions .tq__title,
.atlas-slide--three-questions .tq__subtitle {
  margin: 0;
  font-family: var(--font-family-inspire);
  font-style: normal;
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
  white-space: nowrap;
}

.atlas-slide--three-questions .tq__title {
  width: 1727px;
  color: var(--tq-title);
  font-size: 56px;
  line-height: 56px;
}

.atlas-slide--three-questions .tq__subtitle {
  width: 1828px;
  color: var(--tq-subtitle);
  font-size: 36px;
  line-height: 56px;
}

/* ---------- The three inputs ---------------------------------------------
 * Figma sets each row's left edge separately (64, 60, 63). Those are kept
 * rather than averaged, because the node is the source of truth. */
.atlas-slide--three-questions .tq__block {
  position: absolute;
  display: flex;
  align-items: center;
}

.atlas-slide--three-questions .tq__block--details  { top: 275px; left: 64px; }
.atlas-slide--three-questions .tq__block--lines    { top: 523px; left: 60px; }
.atlas-slide--three-questions .tq__block--premiums { top: 769px; left: 63px; }

.atlas-slide--three-questions .tq__accent {
  flex: none;
  width: 2px;
  height: 131px;
  background: linear-gradient(180deg, #6CB8FF 0%, #1677FF 100%);
  box-shadow: 0 0 12px rgba(41, 151, 255, 0.22);
}

.atlas-slide--three-questions .tq__card {
  flex: none;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 180px;
  height: 160px;
  padding: 24px 48px;
  border: 1px solid rgba(75, 90, 107, 0.4);
  border-radius: 16px;
  background: linear-gradient(41.24deg,
    #303946 1%, #202833 12.3%, #27313D 63.2%);
  box-shadow:
    0 10px 14px rgba(0, 0, 0, 0.32),
    inset 0 1px 12px rgba(255, 255, 255, 0.08);
}

.atlas-slide--three-questions .tq__icon {
  flex: none;
  display: block;
  width: 64px;
  height: 64px;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

.atlas-slide--three-questions .tq__card-label {
  margin: 0;
  color: var(--tq-card-label);
  font-family: var(--font-family-inspire);
  font-size: 24px;
  font-style: normal;
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  line-height: normal;
  text-align: center;
  white-space: nowrap;
}

/* max-content keeps each question on the single line the Figma node shows,
 * whatever the substituted face measures. */
.atlas-slide--three-questions .tq__copy {
  flex: none;
  /* Figma nests the accent bar against its card and puts the 56px gap
   * only before the text, so the gap lives here rather than on the row. */
  margin-left: 56px;
  width: max-content;
}

.atlas-slide--three-questions .tq__question,
.atlas-slide--three-questions .tq__detail {
  margin: 0;
  color: #F5F5F7;
  font-size: 32px;
  font-style: normal;
  line-height: 1.2;
  white-space: nowrap;
}

.atlas-slide--three-questions .tq__question {
  font-family: var(--font-family-inspire);
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
}

.atlas-slide--three-questions .tq__detail {
  font-family: var(--font-family-inspire);
  font-weight: var(--font-quiet); /* InspireTWDC Light 300 */
}

/* ---------- Operators ----------------------------------------------------- */
.atlas-slide--three-questions .tq__plus {
  position: absolute;
  left: 136px;
  display: block;
  width: 36px;
  height: 36px;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

.atlas-slide--three-questions .tq__plus--1 { top: 461px; }
.atlas-slide--three-questions .tq__plus--2 { top: 708px; }

.atlas-slide--three-questions .tq__equals {
  position: absolute;
  top: 560px;
  left: 1148px;
  display: block;
  width: 56px;
  height: 56px;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* ---------- The answer ----------------------------------------------------
 * Card and supporting line share one box so they arrive as one thing and
 * scale from one origin. */
.atlas-slide--three-questions .tq__result {
  position: absolute;
  top: 477px;
  left: 1250px;
  width: 376px;
  height: 272px;
}

.atlas-slide--three-questions .tq__result-card {
  position: absolute;
  top: 0;
  left: 73px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 9.6px;
  width: 230.4px;
  height: 192px;
  padding: 19.2px 57.6px;
  border: 1.5px solid var(--tq-result-border);
  border-radius: 19.2px;
  background: var(--tq-result-surface);
  box-shadow:
    0 12px 16.8px rgba(0, 0, 0, 0.32),
    inset 0 1.2px 14.4px rgba(255, 255, 255, 0.08);
}

.atlas-slide--three-questions .tq__result-icon {
  flex: none;
  display: block;
  width: 86.4px;
  height: 86.4px;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

.atlas-slide--three-questions .tq__result-label {
  margin: 0;
  color: var(--tq-result-label);
  font-family: var(--font-family-inspire);
  font-size: 28.8px;
  font-style: normal;
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  line-height: normal;
  text-align: center;
  white-space: nowrap;
}

.atlas-slide--three-questions .tq__result-note {
  position: absolute;
  top: 234px;
  left: 0;
  width: 376px;
  margin: 0;
  color: var(--tq-title);
  font-family: var(--font-family-inspire);
  font-size: 32px;
  font-style: normal;
  font-weight: var(--font-body); /* InspireTWDC Medium 500 */
  line-height: 1.2;
  white-space: nowrap;
}

.atlas-slide--three-questions .tq__sr {
  position: absolute;
  top: 0;
  left: 0;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.atlas-slide--three-questions .tq__logo {
  position: absolute;
  top: 997px;
  left: 1826px;
  display: block;
  width: 36px;
  height: 41px;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* ---------- Reveal states -------------------------------------------------
 * Every element holds its final box from the first paint; only opacity and
 * transform move, so nothing reflows across the run. */
.atlas-slide--three-questions [data-tq-reveal] {
  opacity: 0;
}

.atlas-slide--three-questions .tq__header[data-tq-reveal] {
  transform: translateY(7px);
  transition:
    opacity var(--tq-header-duration) var(--tq-ease),
    transform var(--tq-header-duration) var(--tq-ease);
}

.atlas-slide--three-questions .tq__block[data-tq-reveal] {
  transform: translateY(8px);
  transition:
    opacity var(--tq-group-duration) var(--tq-ease),
    transform var(--tq-group-duration) var(--tq-ease);
}

.atlas-slide--three-questions .tq__plus[data-tq-reveal] {
  transition: opacity var(--tq-mark-duration) var(--tq-ease);
}

/* The equals sign travels in from the inputs it follows. */
.atlas-slide--three-questions .tq__equals[data-tq-reveal] {
  transform: translateX(-12px);
  transition:
    opacity var(--tq-equals-duration) var(--tq-ease),
    transform var(--tq-equals-duration) var(--tq-ease);
}

.atlas-slide--three-questions .tq__result[data-tq-reveal] {
  transform: translateX(8px) scale(0.96);
  transform-origin: center center;
  transition:
    opacity var(--tq-result-duration) var(--tq-ease),
    transform var(--tq-result-duration) var(--tq-ease);
}

.atlas-slide--three-questions [data-tq-reveal].is-revealed {
  opacity: 1;
}

.atlas-slide--three-questions .tq__header[data-tq-reveal].is-revealed,
.atlas-slide--three-questions .tq__block[data-tq-reveal].is-revealed,
.atlas-slide--three-questions .tq__equals[data-tq-reveal].is-revealed,
.atlas-slide--three-questions .tq__result[data-tq-reveal].is-revealed {
  transform: none;
}

/* One pass of gold as the answer settles, from and back to the resting
 * Figma state. It is a single pass, not a pulse. */
.atlas-slide--three-questions
  .tq__result[data-tq-reveal].is-revealed
  .tq__result-card {
  animation: tq-result-emphasis 760ms var(--tq-ease) 260ms 1 both;
}

@keyframes tq-result-emphasis {
  0% {
    box-shadow:
      0 12px 16.8px rgba(0, 0, 0, 0.32),
      inset 0 1.2px 14.4px rgba(255, 255, 255, 0.08),
      0 0 0 0 rgba(189, 151, 94, 0);
  }
  50% {
    box-shadow:
      0 12px 16.8px rgba(0, 0, 0, 0.32),
      inset 0 1.2px 14.4px rgba(255, 255, 255, 0.08),
      0 0 22px 4px rgba(189, 151, 94, 0.34);
  }
  100% {
    box-shadow:
      0 12px 16.8px rgba(0, 0, 0, 0.32),
      inset 0 1.2px 14.4px rgba(255, 255, 255, 0.08),
      0 0 0 0 rgba(189, 151, 94, 0);
  }
}

/* Reduced motion keeps all six states and the manual pause, because the
 * order and the wait are the argument. Only the travel, the scale and the
 * gold pass are dropped. */
@media (prefers-reduced-motion: reduce) {
  .atlas-slide--three-questions .tq__header[data-tq-reveal],
  .atlas-slide--three-questions .tq__block[data-tq-reveal],
  .atlas-slide--three-questions .tq__plus[data-tq-reveal],
  .atlas-slide--three-questions .tq__equals[data-tq-reveal],
  .atlas-slide--three-questions .tq__result[data-tq-reveal] {
    transform: none;
    transition: opacity 120ms linear;
  }

  .atlas-slide--three-questions
    .tq__result[data-tq-reveal].is-revealed
    .tq__result-card {
    animation: none;
  }
}

/* ============================================================================
 * SLIDE 5: Why do line items need to be in the Rate Card?
 * Figma node 600:1553, authored at the deck's native 1920 x 1080 size.
 * Zooms in on the Line Item building block from the previous slide:
 * Rate Card (framework) + Line Item (sellable item + base rate) =
 * Consistent Pricing.
 * ========================================================================== */
.atlas-slide--line-need {
  --wln-background: #020024;
  --wln-title: #FFFFFF;
  --wln-body: #B8BCC7;
  --wln-card-text: #F5F7FA;
  --wln-takeaway: #ECEEEE;
  --wln-icon: #21A5F6;
  --wln-card-border: rgba(75, 90, 107, 0.4);
  overflow: hidden;
  border-radius: 40px;
  background: var(--wln-background);
}

.atlas-slide--line-need .wln {
  position: absolute;
  inset: 0;
  overflow: hidden;
  color: var(--wln-title);
  font-family: "InspireTWDC", "MultiplaneTWDC Display", "Open Sans", sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: geometricPrecision;
}

.atlas-slide--line-need .wln__header {
  position: absolute;
  top: 88px;
  left: 63px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 1828px;
  gap: 8px;
}

.atlas-slide--line-need .wln__title,
.atlas-slide--line-need .wln__subtitle,
.atlas-slide--line-need .wln__card-label,
.atlas-slide--line-need .wln__desc,
.atlas-slide--line-need .wln__takeaway {
  margin: 0;
  font-style: normal;
}

.atlas-slide--line-need .wln__title {
  width: 1727px;
  color: var(--wln-title);
  font-family: var(--font-family-inspire);
  font-size: 56px;
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  line-height: 56px;
  white-space: nowrap;
}

.atlas-slide--line-need .wln__subtitle {
  width: 1808px;
  color: var(--wln-title);
  font-family: var(--font-family-inspire);
  font-size: 36px;
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  line-height: 56px;
}

.atlas-slide--line-need .wln__card {
  position: absolute;
  top: 360px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 352px;
  height: 237.6px;
  border: 1.32px solid var(--wln-card-border);
  border-radius: 21.12px;
  background:
    linear-gradient(
      33.652deg,
      rgb(48, 57, 70) 0.998%,
      rgb(32, 40, 51) 21.967%,
      rgb(39, 49, 61) 104.5%
    );
  box-shadow:
    0 13.2px 18.48px rgba(0, 0, 0, 0.32),
    inset 0 1.32px 15.84px rgba(255, 255, 255, 0.08);
}

.atlas-slide--line-need .wln__card--rate-card { left: 92px; }
.atlas-slide--line-need .wln__card--line-item { left: 632px; }
.atlas-slide--line-need .wln__card--pricing   { left: 1185px; }

.atlas-slide--line-need .wln__card-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10.56px;
}

.atlas-slide--line-need .wln__card-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 72px;
  height: 72px;
}

.atlas-slide--line-need .wln__card-icon img {
  display: block;
  max-width: none;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

.atlas-slide--line-need .wln__card-label {
  color: var(--wln-card-text);
  font-family: var(--font-family-inspire);
  font-size: 28px;
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  line-height: normal;
  text-align: center;
  white-space: nowrap;
}

.atlas-slide--line-need .wln__operator {
  position: absolute;
  top: 360px;
  display: grid;
  place-items: center;
  height: 237.6px;
}

.atlas-slide--line-need .wln__operator img {
  display: block;
  max-width: none;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

.atlas-slide--line-need .wln__operator--plus {
  left: 518px;
  width: 39.6px;
}

.atlas-slide--line-need .wln__operator--equals {
  left: 1058px;
  width: 52.8px;
}

.atlas-slide--line-need .wln__desc {
  position: absolute;
  top: 629px;
  transform: translateX(-50%);
  width: 460px;
  color: var(--wln-body);
  font-family: var(--font-family-inspire);
  font-size: 24px;
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
  line-height: 1.2;
  text-align: center;
  white-space: nowrap;
}

.atlas-slide--line-need .wln__desc--rate-card { left: 268px; }
.atlas-slide--line-need .wln__desc--line-item { left: 808px; }
.atlas-slide--line-need .wln__desc--pricing   { left: 1361px; }

.atlas-slide--line-need .wln__takeaway {
  position: absolute;
  top: 764px;
  left: 63px;
  width: 1794px;
  color: var(--wln-takeaway);
  font-family: var(--font-family-inspire);
  font-size: 36px;
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  line-height: normal;
  white-space: nowrap;
}

.atlas-slide--line-need .wln__logo {
  position: absolute;
  top: 997px;
  left: 1826px;
  width: 36px;
  height: 41px;
  display: block;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* ============================================================================
 * SLIDE 3: Think of Disney Advertising as a supermarket
 * Figma node 620:20510, authored at the deck's native 1920 x 1080 size.
 *
 * Three columns left to right: the store, the shelf space in it, and the
 * product on the shelf. Each column is a drawing with a small grey
 * category label and a large white business label centred under it.
 *
 * Reveal: the heading arrives on its own (title, then subtitle), then the
 * presenter brings in one whole column per gesture. A column is a single
 * reveal group holding its arrow, its drawing and both of its captions,
 * so a piece of a column can never arrive without the rest of it.
 * initAtlasSupermarket() in app.js owns the steps.
 * ========================================================================== */
.atlas-slide--supermarket {
  /* Reveal contract shared with app.js and qa_supermarket_slide.py. */
  --sup-text-duration: 440ms;
  --sup-group-duration: 440ms;
  --sup-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  overflow: hidden;
  border-radius: 40px;
  background: #020024;
}

.atlas-slide--supermarket .sup {
  position: absolute;
  inset: 0;
  overflow: hidden;
  color: #FFFFFF;
  font-family: "InspireTWDC", "MultiplaneTWDC Display", "Open Sans", sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: geometricPrecision;
}

/* ---------- Header ----------------------------------------------------- */
.atlas-slide--supermarket .sup__header {
  position: absolute;
  top: 88px;
  left: 63px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 1828px;
  gap: 8px;
}

.atlas-slide--supermarket .sup__title,
.atlas-slide--supermarket .sup__subtitle {
  margin: 0;
  color: #FFFFFF;
  /* Figma sets both lines in the Heavy face on this slide, unlike the
   * titles elsewhere in the deck that use Black. */
  font-family: var(--font-family-inspire);
  font-style: normal;
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
  white-space: nowrap;
}

.atlas-slide--supermarket .sup__title {
  width: 1727px;
  font-size: 56px;
  line-height: 56px;
}

.atlas-slide--supermarket .sup__subtitle {
  width: 1747px;
  font-size: 36px;
  line-height: 56px;
}

/* ---------- The three columns ------------------------------------------
 *  Both the picture and each column inside it span the slide, so every
 *  drawing, caption and arrow keeps the Figma frame's own coordinates and
 *  one transform can move a whole column at once. */
.atlas-slide--supermarket .sup__visual {
  position: absolute;
  inset: 0;
}

.atlas-slide--supermarket .sup__group {
  position: absolute;
  inset: 0;
}

.atlas-slide--supermarket .sup__art {
  position: absolute;
  top: 343px;
  display: block;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

.atlas-slide--supermarket .sup__art--store {
  left: 87px;
  width: 369.957px;
  height: 296px;
}

.atlas-slide--supermarket .sup__art--shelves {
  left: 704px;
  width: 309.4005px;
  height: 312.8754px;
}

.atlas-slide--supermarket .sup__art--produce {
  left: 1296px;
  width: 333.7107px;
  height: 299px;
}

/* Both caption rows share one top per row and the same three centre-x
 * values (the drawing midpoints), so the columns can only move together
 * and a caption can never drift off its own drawing. */
.atlas-slide--supermarket .sup__label-small,
.atlas-slide--supermarket .sup__label {
  position: absolute;
  margin: 0;
  width: max-content;
  transform: translateX(-50%);
  text-align: center;
  white-space: nowrap;
  font-style: normal;
  font-weight: 900;
}

.atlas-slide--supermarket .sup__label-small {
  top: 747px;
  color: var(--ads-text-tertiary, #51585B);
  font-family: var(--font-family-inspire);
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  font-size: 32px;
  line-height: 38.4px;
}

.atlas-slide--supermarket .sup__label {
  top: 798px;
  height: 40px;
  color: #FFFFFF;
  font-family: var(--font-family-inspire);
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  font-size: 40px;
  line-height: 40px;
}

/* left = the midpoint of the drawing above, which is where Figma centres
 * both captions too. */
.atlas-slide--supermarket .sup__label-small--store,
.atlas-slide--supermarket .sup__label--store { left: 271.9785px; }
.atlas-slide--supermarket .sup__label-small--shelves,
.atlas-slide--supermarket .sup__label--shelves { left: 858.7003px; }
.atlas-slide--supermarket .sup__label-small--produce,
.atlas-slide--supermarket .sup__label--produce { left: 1462.8554px; }


/* ---------- Reveal states ----------------------------------------------
 * Everything holds its final box from the first paint, so nothing reflows
 * while the slide comes in. The heading's two lines lift a few pixels as
 * they arrive; the three columns fade and nothing more. */
.atlas-slide--supermarket [data-sup-reveal] {
  opacity: 0;
}

.atlas-slide--supermarket .sup__title[data-sup-reveal],
.atlas-slide--supermarket .sup__subtitle[data-sup-reveal] {
  transform: translateY(7px);
  transition:
    opacity var(--sup-text-duration) var(--sup-ease),
    transform var(--sup-text-duration) var(--sup-ease);
}

/* A column arrives as one piece: the wrapper carries the fade and the
 * small lift for the whole column, so its drawing and both captions can
 * only move together and no part of it can lead the rest. The captions
 * centre themselves with their own translateX, which is why the lift
 * lives out here rather than on them. */
.atlas-slide--supermarket .sup__group[data-sup-reveal] {
  transform: translateY(8px);
  transition:
    opacity var(--sup-group-duration) var(--sup-ease) 0ms,
    transform var(--sup-group-duration) var(--sup-ease) 0ms;
}

.atlas-slide--supermarket [data-sup-reveal].is-revealed {
  opacity: 1;
  transform: none;
}

/* Reduced motion keeps the reading order, heading then one column per
 * gesture, left to right. Only the travel is dropped, so a column still
 * arrives whole, just without the lift. */
@media (prefers-reduced-motion: reduce) {
  .atlas-slide--supermarket .sup__title[data-sup-reveal],
  .atlas-slide--supermarket .sup__subtitle[data-sup-reveal],
  .atlas-slide--supermarket .sup__group[data-sup-reveal] {
    transform: none;
    transition: opacity 120ms linear 0ms;
  }
}

/* ============================================================================
 * SLIDE 4: Buyers can purchase through different deal types
 * Figma node 736:479, authored at the deck's native 1920 x 1080 size.
 *
 * Two columns side by side, each an illustration with its deal name, its
 * blue one-line summary and a supporting line underneath. The two are a
 * comparison, so nothing sits between them: no divider, no card, no arrow.
 *
 * Reveal: three stages, heading then Upfront then Scatter, so the deck
 * reads left to right. Each column is one reveal target, so an
 * illustration can never arrive without its own copy.
 * initAtlasUpfrontScatter() in app.js owns the timings.
 * ========================================================================== */
.atlas-slide--upfront-scatter {
  --usc-lede: #6CB8FF;
  /* Reveal contract shared with app.js and qa_upfront_scatter_slide.py. */
  --usc-header-duration: 500ms;
  --usc-group-duration: 580ms;
  --usc-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  overflow: hidden;
  border-radius: 40px;
  background: #020024;
}

.atlas-slide--upfront-scatter .usc {
  position: absolute;
  inset: 0;
  overflow: hidden;
  color: #FFFFFF;
  font-family: "InspireTWDC", "MultiplaneTWDC Display", "Open Sans", sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: geometricPrecision;
}

/* ---------- Header ----------------------------------------------------- */
.atlas-slide--upfront-scatter .usc__header {
  position: absolute;
  top: 88px;
  left: 63px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 1828px;
  gap: 8px;
}

.atlas-slide--upfront-scatter .usc__title,
.atlas-slide--upfront-scatter .usc__subtitle {
  margin: 0;
  color: #FFFFFF;
  font-family: var(--font-family-inspire);
  font-style: normal;
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
  white-space: nowrap;
}

.atlas-slide--upfront-scatter .usc__title {
  width: 1727px;
  font-size: 56px;
  line-height: 56px;
}

.atlas-slide--upfront-scatter .usc__subtitle {
  width: 1747px;
  font-size: 36px;
  line-height: 56px;
}

/* ---------- The two deal-type columns -----------------------------------
 *  Figma sizes the two illustrations optically rather than identically, so
 *  each column keeps its own width and its own artwork height. */
/* The scenes are drawn 9% larger than the size this slide was first built
 * at. The extra height is taken from the empty band above them rather than
 * from the copy: the column starts higher, the copy underneath stays on the
 * line it already sat on, and the gap between artwork and heading lands at
 * 28px. Each column also keeps the horizontal centre it had, so the two
 * halves stay balanced.
 *
 * The scale is applied to the reserved box, not to the visible artwork
 * alone, so the room, every pose and the clipping bounds grow together. */
.atlas-slide--upfront-scatter .usc__group {
  position: absolute;
  top: 269.7px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 28px;
}

.atlas-slide--upfront-scatter .usc__group--upfront {
  left: 240.6px;
  width: 542.3px;
}

.atlas-slide--upfront-scatter .usc__group--scatter {
  left: 1069.6px;
  width: 542.0px;
}

/* The room image and the four pose cut-outs share one box, so a pose can
 * never spill past the artwork it belongs to. */
.atlas-slide--upfront-scatter .usc__scene {
  position: relative;
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.atlas-slide--upfront-scatter .usc__group--upfront .usc__scene {
  width: 542.3px;
  height: 488.3px;
}

.atlas-slide--upfront-scatter .usc__group--scatter .usc__scene {
  width: 542.0px;
  height: 487.5px;
}

.atlas-slide--upfront-scatter .usc__art {
  display: block;
  flex: 0 0 auto;
  max-width: none;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* The artwork is 846 x 929 per scene, a taller shape than the deck has room
 * for. It is drawn at the column's full width and compressed to 82% of its
 * height. This is the one place that squash is applied: the SVG carries
 * preserveAspectRatio="none", so the room and all four poses compress with
 * it and the poses keep aligning with each other in the artwork's own
 * coordinates. Because the box itself is shorter, the column reserves the
 * shorter height and the label under it moves up to meet it. */
.atlas-slide--upfront-scatter .usc__art--upfront {
  width: 542.3px;
  height: 488.3px;
}

.atlas-slide--upfront-scatter .usc__art--scatter {
  width: 542.0px;
  height: 487.5px;
}

.atlas-slide--upfront-scatter .usc__copy {
  display: flex;
  flex: 0 0 auto;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

.atlas-slide--upfront-scatter .usc__group--upfront .usc__copy { width: 401px; }
.atlas-slide--upfront-scatter .usc__group--scatter .usc__copy { width: 453px; }

/* Sized to the text rather than to Figma's copy box: the supporting line is
 * wider than the box it sits in, and centred text that overflows a fixed
 * box spills only to the right. As a flex item it overflows evenly, which
 * is how the frame renders. */
.atlas-slide--upfront-scatter .usc__deal,
.atlas-slide--upfront-scatter .usc__lede,
.atlas-slide--upfront-scatter .usc__note {
  width: max-content;
  margin: 0;
  font-style: normal;
  text-align: center;
  white-space: nowrap;
}

.atlas-slide--upfront-scatter .usc__deal {
  color: #FFFFFF;
  font-family: var(--font-family-inspire);
  font-size: 40px;
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  line-height: 40px;
}

.atlas-slide--upfront-scatter .usc__lede {
  color: var(--usc-lede);
  font-family: var(--font-family-inspire);
  font-size: 32px;
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  line-height: 38.4px;
}

.atlas-slide--upfront-scatter .usc__note {
  color: #FFFFFF;
  font-family: var(--font-family-inspire);
  font-size: 32px;
  font-weight: var(--font-body); /* InspireTWDC Medium 500 */
  line-height: 32px;
}


/* ---------- Reveal states ----------------------------------------------
 * Three targets: the heading (both lines on one element, so they can only
 * arrive together) and one per column. Only the heading arrives on its own;
 * each column is held at opacity 0 until the presenter opens it, so it is
 * genuinely absent rather than dimmed. Everything holds its final box from
 * the first paint, so nothing reflows when a column arrives. */
.atlas-slide--upfront-scatter [data-usc-reveal] {
  opacity: 0;
}

.atlas-slide--upfront-scatter .usc__header[data-usc-reveal] {
  transform: translateY(7px);
  transition:
    opacity var(--usc-header-duration) var(--usc-ease),
    transform var(--usc-header-duration) var(--usc-ease);
}

.atlas-slide--upfront-scatter .usc__group[data-usc-reveal] {
  transform: translateY(10px) scale(0.99);
  transition:
    opacity var(--usc-group-duration) var(--usc-ease),
    transform var(--usc-group-duration) var(--usc-ease);
}

.atlas-slide--upfront-scatter [data-usc-reveal].is-revealed {
  opacity: 1;
  transform: none;
}

/* Reduced motion keeps the three stages, because heading then Upfront then
 * Scatter is the reading order. Only the travel is dropped. */
@media (prefers-reduced-motion: reduce) {
  .atlas-slide--upfront-scatter .usc__header[data-usc-reveal],
  .atlas-slide--upfront-scatter .usc__group[data-usc-reveal] {
    transform: none;
    transition: opacity 120ms linear;
  }
}

/* ============================================================================
 * SLIDE 5: Same product. Different applicable rate.
 * Figma node 739:6035, authored at the deck's native 1920 x 1080 size.
 *
 * One ad on the left, a connector that splits, and the same ad priced two
 * ways on the right. The connector is an inline SVG of separate segments
 * rather than the flat Figma export: the slide's argument is the split, so
 * the line has to be able to draw itself in order.
 *
 * Reveal: header, Same Ad, the first line, the split, both results
 * together, then the footnote. Each result is one target, so a shelf can
 * never be up while its rate is missing.
 * initAtlasSameAdRate() in app.js owns the order and the timings.
 * ========================================================================== */
.atlas-slide--same-ad-rate {
  --sar-rate: #FFBB00;
  --sar-copy: #ECEEEE;
  --sar-wire: #6CB8FF;
  /* Reveal contract shared with app.js and qa_same_ad_rate_slide.py. */
  --sar-header-duration: 500ms;
  --sar-source-duration: 500ms;
  --sar-line-duration: 400ms;
  --sar-spine-duration: 300ms;
  --sar-branch-duration: 400ms;
  --sar-arrow-duration: 250ms;
  /* Click 1 draws the branch: upright, then arms, then heads. */
  --sar-branch-delay: 150ms;
  --sar-arrow-delay: 475ms;
  /* Clicks 2 and 3 each bring one whole result in. */
  --sar-result-duration: 450ms;
  --sar-result-lift: 7px;
  --sar-footnote-duration: 300ms;
  --sar-fill-duration: 360ms;
  --sar-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --sar-fill-ease: cubic-bezier(0.22, 1, 0.36, 1);
  overflow: hidden;
  border-radius: 40px;
  background: #020024;
}

.atlas-slide--same-ad-rate .sar {
  position: absolute;
  inset: 0;
  overflow: hidden;
  color: #FFFFFF;
  font-family: "InspireTWDC", "MultiplaneTWDC Display", "Open Sans", sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: geometricPrecision;
}

/* ---------- Header ----------------------------------------------------- */
.atlas-slide--same-ad-rate .sar__header {
  position: absolute;
  top: 88px;
  left: 63px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 1828px;
  gap: 8px;
}

.atlas-slide--same-ad-rate .sar__title,
.atlas-slide--same-ad-rate .sar__subtitle {
  margin: 0;
  color: #FFFFFF;
  font-family: var(--font-family-inspire);
  font-style: normal;
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
  white-space: nowrap;
}

.atlas-slide--same-ad-rate .sar__title {
  width: 1727px;
  font-size: 56px;
  line-height: 56px;
}

.atlas-slide--same-ad-rate .sar__subtitle {
  width: 1747px;
  font-size: 36px;
  line-height: 56px;
}

/* ---------- Same Ad ----------------------------------------------------- */
.atlas-slide--same-ad-rate .sar__source {
  position: absolute;
  top: 531.792px;
  left: 285.696px;
}

.atlas-slide--same-ad-rate .sar__pumpkin {
  display: block;
  width: 162.02px;
  height: 134.805px;
  max-width: none;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* Centred on the pumpkin's own midpoint rather than pinned to the Figma
 * text-box edge, which Figma measures in a face the deck substitutes. */
.atlas-slide--same-ad-rate .sar__source-label {
  position: absolute;
  /* Figma 739:12632 sits at y 686 on the slide, 154.208 below the pumpkin's
   * own box. Its text carries a leading space there, so the label is pinned
   * to the pumpkin's centre here instead, which lands in the same place. */
  top: 154.208px;
  left: 81.01px;
  width: max-content;
  margin: 0;
  transform: translateX(-50%);
  color: #FFFFFF;
  font-family: var(--font-family-inspire);
  font-size: 33.887px;
  font-style: normal;
  font-weight: var(--font-body); /* InspireTWDC Medium 500 */
  line-height: 34px;
  text-align: center;
  white-space: nowrap;
}

/* ---------- Connector ---------------------------------------------------
 *  Segment coordinates are Figma 739:12654 and 739:12655 rebased onto one
 *  viewBox, so the whole run shares a coordinate system and each piece can
 *  be drawn on its own. The spine is split at the junction so it can open
 *  outward from the point the first line arrives at. */
.atlas-slide--same-ad-rate .sar__wire {
  position: absolute;
  top: 443.88px;
  left: 492.288px;
  width: 362.161px;
  height: 417.228px;
  overflow: visible;
  pointer-events: none;
}

.atlas-slide--same-ad-rate .sar__seg {
  stroke-dasharray: var(--sar-len);
  stroke-dashoffset: var(--sar-len);
}

/* The short line beside the Ad is already there when the slide opens. It is
 * what the branch grows out of, so it never draws. */
.atlas-slide--same-ad-rate .sar__seg--line {
  --sar-len: 177.904;
  stroke-dashoffset: 0;
}

/* The branch is one click, drawn in the order it would be built: the upright
 * opens from the middle towards both junctions, the two arms run right, and
 * the heads arrive as the arms reach them. 615ms end to end. */
.atlas-slide--same-ad-rate .sar__seg--spine-up   { --sar-len: 199.018; }
.atlas-slide--same-ad-rate .sar__seg--spine-down { --sar-len: 200.209; }
.atlas-slide--same-ad-rate .sar__seg--spine-up,
.atlas-slide--same-ad-rate .sar__seg--spine-down {
  transition: stroke-dashoffset var(--sar-spine-duration) var(--sar-ease) 0ms;
}

.atlas-slide--same-ad-rate .sar__seg--branch-up,
.atlas-slide--same-ad-rate .sar__seg--branch-down {
  --sar-len: 177.904;
  transition:
    stroke-dashoffset var(--sar-branch-duration) var(--sar-ease)
      var(--sar-branch-delay);
}

.atlas-slide--same-ad-rate .sar__seg.is-revealed {
  stroke-dashoffset: 0;
}

.atlas-slide--same-ad-rate .sar__joint {
  opacity: 0;
  transition: opacity var(--sar-branch-duration) var(--sar-ease)
    var(--sar-branch-delay);
}

.atlas-slide--same-ad-rate .sar__arrow {
  opacity: 0;
  transition: opacity var(--sar-arrow-duration) var(--sar-ease)
    var(--sar-arrow-delay);
}

.atlas-slide--same-ad-rate .sar__joint.is-revealed,
.atlas-slide--same-ad-rate .sar__arrow.is-revealed {
  opacity: 1;
}

/* ---------- The two priced results --------------------------------------
 *  Each result is one box holding its shelf and its copy, so the two can
 *  only ever arrive together. */
.atlas-slide--same-ad-rate .sar__result {
  position: absolute;
}

.atlas-slide--same-ad-rate .sar__result--upfront {
  top: 267px;
  left: 909.49px;
  width: 635.26px;
  height: 305.184px;
}

.atlas-slide--same-ad-rate .sar__result--scatter {
  top: 676.82px;
  left: 905.26px;
  width: 639.49px;
  height: 305.184px;
}

.atlas-slide--same-ad-rate .sar__shelf {
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  width: 211.964px;
  height: 305.184px;
  max-width: none;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* The Scatter shelf is inline so its pumpkins can be animated one at a
 * time. They start out beyond the shelf's own box, next to the connector,
 * so the shelf must not crop them on the way in. */
.atlas-slide--same-ad-rate .sar__shelf--scatter {
  overflow: visible;
}

.atlas-slide--same-ad-rate .sar__rate,
.atlas-slide--same-ad-rate .sar__deal,
.atlas-slide--same-ad-rate .sar__placement {
  position: absolute;
  margin: 0;
  font-style: normal;
  white-space: nowrap;
}

.atlas-slide--same-ad-rate .sar__rate {
  display: flex;
  align-items: center;
  gap: 9px;
  height: 48px;
}

.atlas-slide--same-ad-rate .sar__tag {
  display: block;
  flex: 0 0 48px;
  width: 48px;
  height: 48px;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

.atlas-slide--same-ad-rate .sar__rate-value {
  color: var(--sar-rate);
  font-family: var(--font-family-inspire);
  font-size: 32px;
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
  line-height: 32px;
}

.atlas-slide--same-ad-rate .sar__deal {
  color: var(--sar-copy);
  font-family: var(--font-family-inspire);
  font-size: 33.887px;
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  line-height: 40.664px;
}

.atlas-slide--same-ad-rate .sar__placement {
  color: var(--sar-copy);
  font-family: var(--font-family-inspire);
  font-weight: var(--font-body); /* InspireTWDC Medium 500 */
}

/* Figma places the two columns' copy at slightly different offsets and
 * sets the two placement lines a fraction apart. Kept as authored. */
.atlas-slide--same-ad-rate .sar__copy--upfront .sar__rate      { top: 74px;     left: 280.51px; }
.atlas-slide--same-ad-rate .sar__copy--upfront .sar__deal      { top: 126px;    left: 280.51px; }
.atlas-slide--same-ad-rate .sar__copy--upfront .sar__placement { top: 170.66px; left: 280.51px; font-size: 32px; line-height: 38.4px; }

.atlas-slide--same-ad-rate .sar__copy--scatter .sar__rate      { top: 82.18px;  left: 278.74px; }
.atlas-slide--same-ad-rate .sar__copy--scatter .sar__deal      { top: 131.18px; left: 284.74px; }
.atlas-slide--same-ad-rate .sar__copy--scatter .sar__placement { top: 173.18px; left: 284.74px; font-size: 33.887px; line-height: 40.664px; }

/* ---------- Footnote and brand mark -------------------------------------- */
.atlas-slide--same-ad-rate .sar__footnote {
  position: absolute;
  top: 982px;
  left: 63px;
  margin: 0;
  color: #FFFFFF;
  font-family: "InspireTWDC", "MultiplaneTWDC Display", "Open Sans", sans-serif;
  font-size: 24px;
  font-style: italic;
  font-weight: 400;
  line-height: 24px;
  white-space: nowrap;
}


/* ---------- Reveal states -----------------------------------------------
 * The slide opens on the question: heading, the ad, the short line beside
 * it and the footnote are simply there. Everything that answers the
 * question waits for the presenter. Each element holds its final box from
 * the first paint; only opacity, transform and the connector's dash offset
 * move, so nothing reflows while the slide builds. */
.atlas-slide--same-ad-rate .sar__result[data-sar-reveal] {
  opacity: 0;
  transform: translateY(var(--sar-result-lift));
  transition:
    opacity var(--sar-result-duration) var(--sar-ease),
    transform var(--sar-result-duration) var(--sar-ease);
}

.atlas-slide--same-ad-rate .sar__result[data-sar-reveal].is-revealed {
  opacity: 1;
  transform: none;
}

/* One pass of gold across both rates once they have settled. */
.atlas-slide--same-ad-rate
  .sar__result.is-revealed
  .sar__rate-value {
  animation: sar-rate-emphasis 760ms var(--sar-ease) 220ms 1 both;
}

@keyframes sar-rate-emphasis {
  0%   { text-shadow: none; }
  45%  { text-shadow: 0 0 14px rgba(255, 187, 0, 0.45); }
  100% { text-shadow: none; }
}

/* ---------- The ad reaching the Scatter shelf ----------------------------
 * The third click brings the Scatter shelf in holding only the other
 * produce, waits long enough for the room to notice the ad is not on it,
 * then sends ten pumpkins down the lower branch of the connector into the
 * open positions. That is the whole point of the slide, that the same ad
 * can be bought into this placement later, so the shelf has to be seen
 * filling rather than simply appearing full. They arrive from the bottom
 * of the shelf upward, reordered wherever a strictly bottom-up run would
 * send one across a pumpkin already placed.
 *
 * Each pumpkin is drawn twice from one definition. The resting copy sits in
 * its original place in the artwork, where the shelf boards in front of it
 * still cover part of it. The flying copy sits on top of everything, which
 * is the only way the trip across the shelf stays visible: in its own layer
 * a pumpkin disappears behind the boards for most of the way. They share
 * one transform track, so they are never a pixel apart, and the handoff is
 * a 110ms crossfade at the end. What the eye reads is a pumpkin settling in
 * behind the shelf lip.
 *
 * Timings are the contract shared with qa_same_ad_rate_slide.py: the
 * shelf takes 450ms, the pause 600ms, then 360ms per pumpkin 125ms
 * apart, and the rate 150ms after the last one settles. 2935ms in all.
 * ------------------------------------------------------------------------ */
.atlas-slide--same-ad-rate .sar__shelf--scatter [data-sar-pumpkin] {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: 50% 65%;
}

.atlas-slide--same-ad-rate .sar__shelf--scatter [data-sar-pumpkin="1"] {
  --sar-from-x: -109.06px;
  --sar-from-y: -80.46px;
  --sar-delay: 1050ms;
}

.atlas-slide--same-ad-rate .sar__shelf--scatter [data-sar-pumpkin="2"] {
  --sar-from-x: -224.46px;
  --sar-from-y: -18.61px;
  --sar-delay: 1175ms;
}

.atlas-slide--same-ad-rate .sar__shelf--scatter [data-sar-pumpkin="3"] {
  --sar-from-x: -186.36px;
  --sar-from-y: -36.66px;
  --sar-delay: 1300ms;
}

.atlas-slide--same-ad-rate .sar__shelf--scatter [data-sar-pumpkin="4"] {
  --sar-from-x: -227.46px;
  --sar-from-y: 33.64px;
  --sar-delay: 1425ms;
}

.atlas-slide--same-ad-rate .sar__shelf--scatter [data-sar-pumpkin="5"] {
  --sar-from-x: -149.96px;
  --sar-from-y: -11.86px;
  --sar-delay: 1550ms;
}

.atlas-slide--same-ad-rate .sar__shelf--scatter [data-sar-pumpkin="6"] {
  --sar-from-x: -104.76px;
  --sar-from-y: -35.56px;
  --sar-delay: 1675ms;
}

.atlas-slide--same-ad-rate .sar__shelf--scatter [data-sar-pumpkin="7"] {
  --sar-from-x: -217.76px;
  --sar-from-y: 70.94px;
  --sar-delay: 1800ms;
}

.atlas-slide--same-ad-rate .sar__shelf--scatter [data-sar-pumpkin="8"] {
  --sar-from-x: -178.46px;
  --sar-from-y: 51.64px;
  --sar-delay: 1925ms;
}

.atlas-slide--same-ad-rate .sar__shelf--scatter [data-sar-pumpkin="9"] {
  --sar-from-x: -175.56px;
  --sar-from-y: 97.14px;
  --sar-delay: 2050ms;
}

.atlas-slide--same-ad-rate .sar__shelf--scatter [data-sar-pumpkin="10"] {
  --sar-from-x: -139.76px;
  --sar-from-y: 75.84px;
  --sar-delay: 2175ms;
}

.atlas-slide--same-ad-rate
  .sar__shelf--scatter.is-revealed
  [data-sar-role="flier"] {
  animation:
    sar-pumpkin-glide var(--sar-fill-duration) var(--sar-fill-ease)
      var(--sar-delay) 1 both,
    sar-pumpkin-flier var(--sar-fill-duration) linear
      var(--sar-delay) 1 both;
}

.atlas-slide--same-ad-rate
  .sar__shelf--scatter.is-revealed
  [data-sar-role="rest"] {
  animation:
    sar-pumpkin-glide var(--sar-fill-duration) var(--sar-fill-ease)
      var(--sar-delay) 1 both,
    sar-pumpkin-rest var(--sar-fill-duration) linear
      var(--sar-delay) 1 both;
}

@keyframes sar-pumpkin-glide {
  0% {
    transform: translate(var(--sar-from-x), var(--sar-from-y)) scale(0.95);
  }
  76.4% { transform: translate(0, -2px) scale(1); }
  100%  { transform: translate(0, 0) scale(1); }
}

/* Up to the handoff the flying copy carries the pumpkin. */
@keyframes sar-pumpkin-flier {
  0%   { opacity: 0; }
  33.3% { opacity: 1; }
  82%  { opacity: 1; }
  100% { opacity: 0; }
}

/* After it, the copy that the shelf can cover does. */
@keyframes sar-pumpkin-rest {
  0%   { opacity: 0; }
  82%  { opacity: 0; }
  100% { opacity: 1; }
}

/* Once the shelf is stocked, the rate that applies to it takes one beat of
 * attention. It is already on screen, so this is a dip and recovery rather
 * than an entrance, and it happens once. */
.atlas-slide--same-ad-rate
  .sar__copy--scatter
  .sar__rate[data-sar-reveal].is-revealed {
  animation: sar-rate-arrive 250ms var(--sar-ease) 2685ms 1 both;
  transform-origin: left center;
}

@keyframes sar-rate-arrive {
  0%   { opacity: 1;    transform: scale(1); }
  9%   { opacity: 0.75; transform: scale(0.97); }
  100% { opacity: 1;    transform: scale(1); }
}

/* Reduced motion keeps the six stages, because the order is the argument.
 * The travel, the path drawing and the gold pass are dropped. */
@media (prefers-reduced-motion: reduce) {
  .atlas-slide--same-ad-rate {
    --sar-branch-delay: 0ms;
    --sar-arrow-delay: 0ms;
  }

  .atlas-slide--same-ad-rate .sar__result[data-sar-reveal] {
    transform: none;
    transition: opacity 120ms linear;
  }

  .atlas-slide--same-ad-rate .sar__seg {
    stroke-dasharray: none;
    stroke-dashoffset: 0;
    opacity: 0;
    transition: opacity 120ms linear;
  }

  /* The short line beside the Ad is part of the opening state, so it is
   * never waiting on a class to appear. */
  .atlas-slide--same-ad-rate .sar__seg--line {
    opacity: 1;
  }

  .atlas-slide--same-ad-rate .sar__seg.is-revealed {
    opacity: 1;
  }

  .atlas-slide--same-ad-rate .sar__joint,
  .atlas-slide--same-ad-rate .sar__arrow {
    transition: opacity 120ms linear;
  }

  .atlas-slide--same-ad-rate .sar__result.is-revealed .sar__rate-value {
    animation: none;
  }

  /* The Scatter shelf still fills after the same beat, but nothing travels:
   * the pumpkins simply appear where they belong, together. Only the copy
   * that the shelf can cover is used, so the finished shelf is identical. */
  .atlas-slide--same-ad-rate
    .sar__shelf--scatter.is-revealed
    [data-sar-role="flier"] {
    animation: none;
    opacity: 0;
  }

  .atlas-slide--same-ad-rate
    .sar__shelf--scatter.is-revealed
    [data-sar-role="rest"] {
    animation: sar-pumpkin-appear 250ms linear 720ms 1 both;
    transform: none;
  }

  .atlas-slide--same-ad-rate
    .sar__copy--scatter
    .sar__rate[data-sar-reveal].is-revealed {
    animation: sar-rate-appear 250ms linear 1120ms 1 both;
    transform: none;
  }
}

@keyframes sar-pumpkin-appear {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes sar-rate-appear {
  0%   { opacity: 1; }
  10%  { opacity: 0.7; }
  100% { opacity: 1; }
}

/* ============================================================================
 * SLIDE 6: Each line item is priced as the media plan is built.
 * Figma node 638:59738, authored at the deck's native 1920 x 1080.
 *
 * One line item walks the length of the slide. It starts as the advertiser's
 * ad, picks up an offering at ICM, picks up an audience at TOM, is priced at
 * RCM, and arrives back at Core Planning carrying its rate. The four small
 * Core Planning windows between the stops are not four cards: they are the
 * same window photographed four times, each holding a little more than the
 * one before it.
 *
 * Every coordinate below is the Figma coordinate. Nothing is nudged to make
 * a movement easier, and no animation ends anywhere other than the position
 * Figma specifies, so the finished slide is the Figma frame exactly.
 *
 * The presenter owns the pace: five clicks, one stop each, and the slide is
 * completely still between them.
 * ========================================================================== */

.atlas-slide--media-plan {
  /* Figma's palette for this frame. */
  --mpl-ink: #FFFFFF;
  --mpl-note: #D6A85F;
  --mpl-link: #6CB8FF;
  --mpl-rate: #35454F;

  /* One easing everywhere, so every piece decelerates the same way. */
  --mpl-ease: cubic-bezier(0.22, 1, 0.36, 1);

  --mpl-open: 220ms;    /* the opening state, which is not a click */
  --mpl-fade: 280ms;    /* a label or a shelf arriving */
  /* The handoff. A card and the run beneath it are one gesture, so the two
   * are described together rather than each stage inventing its own timing.
   * The dot lands, the card starts to open a breath later, and the run
   * catches up while the card is still opening, so they finish together. */
  --mpl-dot: 110ms;         /* the starting dot arriving */
  --mpl-card-at: 90ms;      /* the card begins, just after the dot */
  --mpl-card: 680ms;        /* the card wiping open, left edge to right */
  --mpl-shaft-at: 200ms;    /* the run sets off while the card is opening */
  --mpl-shaft: 620ms;       /* and reaches the next stop with it */
  --mpl-head-at: 620ms;     /* the arrowhead as the run reaches the stop */
  --mpl-draw: 800ms;    /* a connector running between two stops */
  --mpl-land: 500ms;    /* the offering settling onto the ICM shelf */
  --mpl-sign: 560ms;    /* the audience sign coming down onto its posts */
  --mpl-place: 720ms;   /* the pumpkin falling 90px onto the scale */
  --mpl-panel: 450ms;   /* the final Core Planning window */
}

.atlas-slide--media-plan .mpl {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

/* ---- heading ------------------------------------------------------------ */
/* Present from the first frame. The short fade is the slide arriving, not a
 * step in the story, so it runs without waiting for anyone. */
.atlas-slide--media-plan .mpl__header {
  position: absolute;
  left: 63px;
  top: 88px;
  width: 1828px;
  animation: mpl-open var(--mpl-open) var(--mpl-ease) 1 both;
}

.atlas-slide--media-plan .mpl__title {
  margin: 0;
  width: 1727px;
  font-family: var(--font-family-inspire);
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
  font-size: 56px;
  line-height: 1;
  color: var(--mpl-ink);
}

.atlas-slide--media-plan .mpl__subtitle {
  margin: 8px 0 0;
  width: 1747px;
  font-family: var(--font-family-inspire);
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
  font-size: 36px;
  line-height: 56px;
  color: var(--mpl-ink);
}

/* ---- stop names and the line under each -------------------------------- */
.atlas-slide--media-plan .mpl__name,
.atlas-slide--media-plan .mpl__note {
  position: absolute;
  margin: 0;
  font-family: var(--font-family-inspire);
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  line-height: 1;
  white-space: nowrap;
}

.atlas-slide--media-plan .mpl__name {
  top: 761px;
  font-size: 36px;
  color: var(--mpl-ink);
}

.atlas-slide--media-plan .mpl__note {
  top: 814px;
  font-size: 24px;
  color: var(--mpl-note);
}

.atlas-slide--media-plan .mpl__name--ad   { left: 119px; }
.atlas-slide--media-plan .mpl__name--icm  { left: 473px; }
.atlas-slide--media-plan .mpl__name--tom  { left: 900px; }
.atlas-slide--media-plan .mpl__name--rcm  { left: 1289px; }
.atlas-slide--media-plan .mpl__name--core { left: 1634px; }

.atlas-slide--media-plan .mpl__note--ad   { left: 62px; }
.atlas-slide--media-plan .mpl__note--icm  { left: 351px; font-size: 26px; }
.atlas-slide--media-plan .mpl__note--tom  { left: 794px; }
.atlas-slide--media-plan .mpl__note--rcm  { left: 1258px; top: 815px; }
.atlas-slide--media-plan .mpl__note--core { left: 1601px; }

.atlas-slide--media-plan .mpl__name--ad,
.atlas-slide--media-plan .mpl__note--ad {
  animation: mpl-open var(--mpl-open) var(--mpl-ease) 1 both;
}

/* ---- artwork ------------------------------------------------------------ */
/* Each piece sits on its Figma box. The layers that belong to one drawing
 * are stacked in Figma's own order so nothing that should be covered ends up
 * on top: at RCM the pumpkin goes down between the pedestal and the lip of
 * the scanner, which is why that illustration ships in three files. */
.atlas-slide--media-plan .mpl__art,
.atlas-slide--media-plan .mpl__drop {
  position: absolute;
  display: block;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

.atlas-slide--media-plan .mpl__art--ad {
  /* Figma reports this node at x=197, which is the figure's right edge
   * rather than its left: the node is flipped horizontally inside the
   * frame, so its origin ends up on the far side of the artwork. The box
   * is therefore 197 - 130 = 67, and the flip itself is baked into
   * line-item-ad.svg so no animation can overwrite it. */
  left: 67px; top: 438px; width: 130px; height: 245px;
  animation: mpl-open var(--mpl-open) var(--mpl-ease) 1 both;
}

.atlas-slide--media-plan .mpl__art--icm     { left: 408px;  top: 415px;     width: 204.081px; height: 293.834px; }
.atlas-slide--media-plan .mpl__drop--icm    { left: 408px;  top: 415px;     width: 204.081px; height: 293.834px; }
.atlas-slide--media-plan .mpl__art--tom     { left: 860px;  top: 412px;     width: 175.509px; height: 314.374px; }
.atlas-slide--media-plan .mpl__drop--tom    { left: 860px;  top: 412px;     width: 175.509px; height: 314.374px; }
.atlas-slide--media-plan .mpl__art--rcm-woman   { left: 1298px; top: 446px;     width: 82.133px;  height: 225.491px; }
.atlas-slide--media-plan .mpl__art--rcm-cart    { left: 1205px; top: 537.491px; width: 242.567px; height: 200.249px; }
.atlas-slide--media-plan .mpl__drop--rcm        { left: 1205px; top: 537.491px; width: 242.567px; height: 200.249px; }
.atlas-slide--media-plan .mpl__art--rcm-scanner { left: 1205px; top: 537.491px; width: 242.567px; height: 200.249px; }
/* Enlarged 10% on request; Figma draws it at 184 square. Everything else
 * about it is Figma's, including where its centre sits, and that centre is
 * not the card row's. Figma hangs the four small windows above the arrow
 * line as annotations and drops this one onto the line itself, so the last
 * connector arrives at its middle rather than clipping a corner. Pulling it
 * up level with the cards breaks that: the arrow ends up entering at 92% of
 * the panel's height, and the return leg lands inside the artwork. So it
 * keeps Figma's centre, and the extra 18.4px of height is split evenly
 * around it: 464 - 9.2 = 454.8. */
.atlas-slide--media-plan .mpl__art--core    { left: 1646.8px; top: 454.8px; width: 202.4px; height: 202.4px; }

/* ---- the line item, four times ----------------------------------------- */
.atlas-slide--media-plan .mpl__card {
  position: absolute;
  top: 440px;
  width: 113px;
  height: 113px;
}

.atlas-slide--media-plan .mpl__card img {
  display: block;
  width: 113px;
  height: 113px;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

.atlas-slide--media-plan .mpl__card--1 { left: 238px; }
.atlas-slide--media-plan .mpl__card--2 { left: 682px; }
.atlas-slide--media-plan .mpl__card--3 { left: 1090px; }
.atlas-slide--media-plan .mpl__card--4 { left: 1496px; }

/* The rate belongs to the fourth window and travels with it. */
.atlas-slide--media-plan .mpl__rate {
  position: absolute;
  left: 22px;
  top: 56px;
  margin: 0;
  font-family: var(--font-family-inspire);
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  font-size: 18px;
  line-height: 1;
  color: var(--mpl-rate);
  white-space: nowrap;
}

/* ---- connectors --------------------------------------------------------- */
/* Every run is the same 160px shaft on the same axis, so they read as one
 * repeated move rather than four different ones. */
.atlas-slide--media-plan .mpl__link {
  position: absolute;
  top: 570px;
  display: block;
  overflow: visible;
}

.atlas-slide--media-plan .mpl__link--1 { left: 216px; }
.atlas-slide--media-plan .mpl__link--2 { left: 653px; }
.atlas-slide--media-plan .mpl__link--3 { left: 1061px; }
.atlas-slide--media-plan .mpl__link--4 { left: 1467px; }

/* ---- the return leg ----------------------------------------------------- */
.atlas-slide--media-plan .mpl__loop {
  position: absolute;
  left: 120px;
  top: 318px;
  display: block;
  overflow: visible;
}

.atlas-slide--media-plan .mpl__loop-label {
  position: absolute;
  left: 759px;
  top: 293px;
  margin: 0;
  font-family: var(--font-family-inspire);
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
  font-size: 24px;
  line-height: 1;
  color: var(--mpl-link);
  white-space: nowrap;
}

/* ---- brand mark --------------------------------------------------------- */

.atlas-slide--media-plan .mpl__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ==========================================================================
 * The five clicks.
 *
 * Anything a click brings on starts hidden and is switched on by
 * .is-revealed. The order inside a click is carried entirely by
 * animation-delay, so one gesture produces one legible chain of events:
 * the window appears and carries the context along, the run draws to the
 * next stop, the stop appears, and the thing the stop contributes settles
 * onto it. Nothing overlaps its own cause.
 * ========================================================================== */

.atlas-slide--media-plan [data-mpl-reveal] {
  opacity: 0;
}

.atlas-slide--media-plan [data-mpl-reveal].is-revealed {
  opacity: 1;
}

/* -- the window, opened left to right ------------------------------------- */
/* The card is uncovered from its left edge to its right, in the same
 * direction as the run beneath it, and leans the last twelve pixels into
 * place as it opens. It is carrying the line item to the next stop, so it
 * should look like it arrives rather than appears; but it travels only far
 * enough to say so, and it never leaves the coordinate Figma puts it on.
 *
 * The coordinate lives on .mpl__card and the movement on the wrapper inside
 * it. Nothing animates the element that holds the layout, so no keyframe can
 * overwrite a position and no card can inherit another element's transform. */
@keyframes mpl-card-in {
  0%   { opacity: 0; visibility: visible; clip-path: inset(0 100% 0 0);
         transform: translate3d(-12px, 4px, 0); }
  30%  { opacity: 1; }
  100% { opacity: 1; visibility: visible; clip-path: inset(0 0 0 0);
         transform: translate3d(0, 0, 0); }
}

.atlas-slide--media-plan .mpl__card[data-mpl-reveal] { opacity: 1; }

.atlas-slide--media-plan .mpl__card-motion {
  opacity: 0;
  visibility: hidden;
  clip-path: inset(0 100% 0 0);
  transform: translate3d(-12px, 4px, 0);
  transform-origin: 0% 50%;
  will-change: transform, clip-path, opacity;
}

.atlas-slide--media-plan .mpl__card.is-revealed .mpl__card-motion {
  animation: mpl-card-in var(--mpl-card) var(--mpl-ease) 1 both;
  animation-delay: var(--mpl-card-at);
}

/* -- a connector running out ---------------------------------------------- */
/* Three pieces on one clock. The dot lands first and is the thing that says
 * the handoff has begun; the shaft is drawn with a dash the length of the
 * line so it grows from the tail; the head waits until the shaft is home.
 *
 * The shaft sets off while the card above it is still opening. That overlap
 * is the whole point: apart, they read as two events, and the card looks
 * like it is waiting for permission. */
.atlas-slide--media-plan .mpl__link .mpl__dot {
  opacity: 0;
  transform: scale(0.55);
  transform-box: fill-box;
  transform-origin: center;
}

.atlas-slide--media-plan .mpl__link .mpl__shaft {
  stroke-dasharray: 150;
  stroke-dashoffset: 150;
}

.atlas-slide--media-plan .mpl__link .mpl__head { opacity: 0; }

@keyframes mpl-dot-in {
  0%   { opacity: 0;   transform: scale(0.55); }
  100% { opacity: 1;   transform: scale(1); }
}

.atlas-slide--media-plan .mpl__link.is-revealed .mpl__dot {
  animation: mpl-dot-in var(--mpl-dot) var(--mpl-ease) 1 both;
}

.atlas-slide--media-plan .mpl__link.is-revealed .mpl__shaft {
  animation: mpl-draw-150 var(--mpl-shaft) var(--mpl-ease) 1 both;
  animation-delay: var(--mpl-shaft-at);
}

.atlas-slide--media-plan .mpl__link.is-revealed .mpl__head {
  animation: mpl-fade var(--mpl-fade) var(--mpl-ease) 1 both;
  animation-delay: var(--mpl-head-at);
}

@keyframes mpl-draw-150 { to { stroke-dashoffset: 0; } }
@keyframes mpl-fade     { from { opacity: 0; } to { opacity: 1; } }
@keyframes mpl-open     { from { opacity: 0; } to { opacity: 1; } }

/* -- something landing on a surface --------------------------------------- */
/* Short, one direction, no rebound. The offering drops the 24px onto the ICM
 * shelf; the audience sign drops 20px onto its posts. */
/* Every one of these begins at zero opacity, and that is not a detail.
 * They are held back behind an animation-delay, and animation-fill-mode:
 * both paints the 0% keyframe for the whole of that delay. A first frame
 * of 0.5 therefore does not read as "about to fall": it reads as a
 * half transparent pumpkin sitting on the shelf, for a second, before
 * anything moves. Start at zero and the wait is empty, which is the point
 * of waiting. */
@keyframes mpl-land-offering {
  0%   { opacity: 0; transform: translate3d(0, -30px, 0); }
  35%  { opacity: 1; }
  100% { opacity: 1; transform: translate3d(0, 0, 0); }
}

@keyframes mpl-land-sign {
  0%   { opacity: 0; transform: translate3d(0, -28px, 0); }
  30%  { opacity: 1; }
  100% { opacity: 1; transform: translate3d(0, 0, 0); }
}

/* At RCM the pumpkin comes down onto the scale from 90px up, far enough
 * that the drop is unmistakable at presentation size. It falls straight,
 * with no sideways drift, and the woman is not touched at all: she is one
 * flat drawing and any attempt to move her arm would deform her.
 *
 * This one gets its own curve. The deck's usual ease leaves at speed and
 * coasts, which over 90px looks like the pumpkin is being lowered on a
 * wire; this one starts slowly and gathers pace, so it reads as falling. */
@keyframes mpl-place-rate {
  0%   { opacity: 0; transform: translate3d(0, -90px, 0); }
  14%  { opacity: 1; }
  100% { opacity: 1; transform: translate3d(0, 0, 0); }
}

.atlas-slide--media-plan .mpl__drop--icm.is-revealed {
  animation: mpl-land-offering var(--mpl-land) var(--mpl-ease) 1 both;
}

.atlas-slide--media-plan .mpl__drop--tom.is-revealed {
  animation: mpl-land-sign var(--mpl-sign) var(--mpl-ease) 1 both;
}

.atlas-slide--media-plan .mpl__drop--rcm.is-revealed {
  animation: mpl-place-rate var(--mpl-place) cubic-bezier(0.3, 0.05, 0.45, 1) 1 both;
}

/* -- the return leg drawing itself ---------------------------------------- */
/* Up from Core Planning, back along the top, down onto the advertiser. The
 * order is the point: it has to read as going back to the beginning. */
.atlas-slide--media-plan .mpl__loop-up      { stroke-dasharray: 102;  stroke-dashoffset: 102; }
.atlas-slide--media-plan .mpl__loop-across  { stroke-dasharray: 1624; stroke-dashoffset: 1624; }
.atlas-slide--media-plan .mpl__loop-down    { stroke-dasharray: 93;   stroke-dashoffset: 93; }
.atlas-slide--media-plan .mpl__loop-dot,
.atlas-slide--media-plan .mpl__loop-head    { opacity: 0; }

@keyframes mpl-draw-102  { to { stroke-dashoffset: 0; } }
@keyframes mpl-draw-1624 { to { stroke-dashoffset: 0; } }
@keyframes mpl-draw-93   { to { stroke-dashoffset: 0; } }

.atlas-slide--media-plan .mpl__loop.is-revealed .mpl__loop-up {
  animation: mpl-draw-102 220ms var(--mpl-ease) 1 both;
}

.atlas-slide--media-plan .mpl__loop.is-revealed .mpl__loop-dot--right {
  animation: mpl-fade 160ms var(--mpl-ease) 1 both;
  animation-delay: 180ms;
}

.atlas-slide--media-plan .mpl__loop.is-revealed .mpl__loop-across {
  animation: mpl-draw-1624 720ms var(--mpl-ease) 1 both;
  animation-delay: 220ms;
}

.atlas-slide--media-plan .mpl__loop.is-revealed .mpl__loop-dot--left {
  animation: mpl-fade 160ms var(--mpl-ease) 1 both;
  animation-delay: 880ms;
}

.atlas-slide--media-plan .mpl__loop.is-revealed .mpl__loop-down {
  animation: mpl-draw-93 320ms var(--mpl-ease) 1 both;
  animation-delay: 940ms;
}

.atlas-slide--media-plan .mpl__loop.is-revealed .mpl__loop-head {
  animation: mpl-fade 160ms var(--mpl-ease) 1 both;
  animation-delay: 1140ms;
}

/* The label rides in beside the line while the line is still running. */
@keyframes mpl-loop-label {
  from { opacity: 0; transform: translateX(8px); }
  to   { opacity: 1; transform: translateX(0); }
}

.atlas-slide--media-plan .mpl__loop-label.is-revealed {
  animation: mpl-loop-label 340ms var(--mpl-ease) 1 both;
  animation-delay: 480ms;
}

/* ==========================================================================
 * When each piece arrives, click by click.
 * ========================================================================== */

/* Click 1: the line item is created and ICM takes delivery of it. */
.atlas-slide--media-plan .mpl__art--icm.is-revealed {
  animation: mpl-fade var(--mpl-fade) var(--mpl-ease) 1 both;
  animation-delay: 560ms;
}
/* 560 + 280 puts the shelf fully up at 840, then it is left alone for a
 * beat so the audience sees an empty shelf before anything lands on it. */
.atlas-slide--media-plan .mpl__drop--icm.is-revealed { animation-delay: 960ms; }
.atlas-slide--media-plan .mpl__name--icm.is-revealed,
.atlas-slide--media-plan .mpl__note--icm.is-revealed {
  animation: mpl-fade var(--mpl-fade) var(--mpl-ease) 1 both;
  animation-delay: 900ms;
}

/* Click 2: the planner picks the offering, and TOM takes it on. */
.atlas-slide--media-plan .mpl__art--tom.is-revealed {
  animation: mpl-fade var(--mpl-fade) var(--mpl-ease) 1 both;
  animation-delay: 980ms;
}
/* The TOM shelf ships with its pumpkin already on it and neither moves.
 * 980 + 280 has both fully up at 1260; the sign waits a beat after that. */
.atlas-slide--media-plan .mpl__drop--tom.is-revealed { animation-delay: 1380ms; }
.atlas-slide--media-plan .mpl__name--tom.is-revealed,
.atlas-slide--media-plan .mpl__note--tom.is-revealed {
  animation: mpl-fade var(--mpl-fade) var(--mpl-ease) 1 both;
  animation-delay: 1320ms;
}

/* Click 3: the planner picks the audience, and RCM is asked for a price. */
.atlas-slide--media-plan .mpl__art--rcm-woman.is-revealed,
.atlas-slide--media-plan .mpl__art--rcm-cart.is-revealed,
.atlas-slide--media-plan .mpl__art--rcm-scanner.is-revealed {
  animation: mpl-fade var(--mpl-fade) var(--mpl-ease) 1 both;
  animation-delay: 980ms;
}
/* The woman, cart and scale are up at 1260 and stay exactly as drawn. Only
 * the pumpkin moves, and only after they have all been seen. */
.atlas-slide--media-plan .mpl__drop--rcm.is-revealed { animation-delay: 1380ms; }
.atlas-slide--media-plan .mpl__name--rcm.is-revealed,
.atlas-slide--media-plan .mpl__note--rcm.is-revealed {
  animation: mpl-fade var(--mpl-fade) var(--mpl-ease) 1 both;
  animation-delay: 1360ms;
}

/* Click 4: the price comes back and Core Planning receives the line item. */
/* A plain fade, and linear on purpose. The deck's ease curve is heavily
 * front loaded, so an eased fade is visually finished long before the
 * animation formally ends, and the second of stillness that follows would
 * read as longer than it is. Linear makes the moment it reaches full
 * opacity and the moment it ends the same moment, which is what the pause
 * after it is measured from. */
.atlas-slide--media-plan .mpl__art--core.is-revealed {
  animation: mpl-fade var(--mpl-panel) linear 1 both;
  animation-delay: 960ms;
}
.atlas-slide--media-plan .mpl__name--core.is-revealed,
.atlas-slide--media-plan .mpl__note--core.is-revealed {
  animation: mpl-fade var(--mpl-fade) var(--mpl-ease) 1 both;
  animation-delay: 1200ms;
}

/* ==========================================================================
 * Reduced motion. Same five clicks, same order, same information: only the
 * travelling is dropped. Everything appears on its Figma coordinate.
 * ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .atlas-slide--media-plan .mpl__header,
  .atlas-slide--media-plan .mpl__art--ad,
  .atlas-slide--media-plan .mpl__name--ad,
  .atlas-slide--media-plan .mpl__note--ad {
    animation: none;
    opacity: 1;
  }

  .atlas-slide--media-plan [data-mpl-reveal] { opacity: 0; }

  .atlas-slide--media-plan [data-mpl-reveal].is-revealed,
  .atlas-slide--media-plan .mpl__drop.is-revealed,
  .atlas-slide--media-plan .mpl__art.is-revealed,
  .atlas-slide--media-plan .mpl__name.is-revealed,
  .atlas-slide--media-plan .mpl__note.is-revealed,
  .atlas-slide--media-plan .mpl__loop-label.is-revealed {
    animation: mpl-fade 160ms linear 1 both;
    animation-delay: 0ms;
    opacity: 1;
    transform: none;
  }

  /* The cards still open, they just do not travel to do it: a short fade
   * with no wipe and no movement, on the same wrapper so nothing is left
   * holding a transform. */
  .atlas-slide--media-plan .mpl__card-motion {
    clip-path: none;
    transform: none;
  }

  .atlas-slide--media-plan .mpl__card.is-revealed .mpl__card-motion {
    animation: mpl-fade 180ms linear 1 both;
    animation-delay: 0ms;
    clip-path: none;
    transform: none;
    visibility: visible;
  }

  /* The run arrives complete rather than being drawn. */
  .atlas-slide--media-plan .mpl__link.is-revealed .mpl__dot {
    animation: mpl-fade 180ms linear 1 both;
    animation-delay: 0ms;
    transform: none;
  }

  .atlas-slide--media-plan .mpl__link .mpl__shaft,
  .atlas-slide--media-plan .mpl__loop-up,
  .atlas-slide--media-plan .mpl__loop-across,
  .atlas-slide--media-plan .mpl__loop-down {
    stroke-dashoffset: 0;
    animation: none;
  }

  .atlas-slide--media-plan .mpl__link.is-revealed .mpl__head,
  .atlas-slide--media-plan .mpl__loop.is-revealed .mpl__loop-dot,
  .atlas-slide--media-plan .mpl__loop.is-revealed .mpl__loop-head {
    animation: none;
    opacity: 1;
  }

}

/* -- Internal views (current design / previous design) ------------
 * One slide, two states. Both panels fill the same box so switching
 * cannot move the presentation frame, and the inactive one is hidden
 * outright rather than merely faded, so it never lands in the tab
 * order or gets read out twice. */
.atlas-slide--connected-workflows .cw {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.atlas-slide--connected-workflows .cw__view {
  position: absolute;
  inset: 0;
  opacity: 1;
  transition: opacity 240ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.atlas-slide--connected-workflows .cw__view[hidden] {
  display: none;
}

.atlas-slide--connected-workflows .cw__view.is-entering {
  opacity: 0;
  transform: translateY(4px);
}

.atlas-slide--connected-workflows .cw__sr {
  position: absolute;
  top: 0;
  left: 0;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* -- The two controls ---------------------------------------------
 * Figma node 741:24681 is a stock 14px ADS secondary button. The
 * border, radius, transparent fill and white text are kept; the type
 * and padding are scaled up together so the control still reads at
 * the back of a room, which the 14px original does not. 30px is the
 * floor that still clears 16px on screen at 1280x720, where the stage
 * scales to 0.54. Both carry a 44px minimum target even though the
 * visible border stays compact. */
.atlas-slide--connected-workflows .cw__toggle,
.atlas-slide--connected-workflows .cw__back {
  position: absolute;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  box-sizing: border-box;
  min-width: 44px;
  min-height: 44px;
  margin: 0;
  padding: 12px 22px;
  border: 1.5px solid #FFFFFF;
  border-radius: 11px;
  background: rgba(255, 255, 255, 0);
  color: #FFFFFF;
  font-family: "Open Sans", sans-serif;
  font-size: 30px;
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background-color 160ms ease,
    border-color 160ms ease;
}

.atlas-slide--connected-workflows .cw__toggle:hover,
.atlas-slide--connected-workflows .cw__back:hover {
  background: rgba(255, 255, 255, 0.12);
}

.atlas-slide--connected-workflows .cw__toggle:active,
.atlas-slide--connected-workflows .cw__back:active {
  background: rgba(255, 255, 255, 0.18);
}

.atlas-slide--connected-workflows .cw__toggle:focus-visible,
.atlas-slide--connected-workflows .cw__back:focus-visible {
  outline: 3px solid #6CB8FF;
  outline-offset: 3px;
}

/* Figma places the toggle at (88, 766). The taller control is pinned
 * by its own top-left so it grows down and right from that anchor. */
.atlas-slide--connected-workflows .cw__toggle { top: 766px; left: 88px; }
.atlas-slide--connected-workflows .cw__back   { top: 56px;  left: 63px; }

/* -- Previous design ----------------------------------------------
 * Figma node 741:19209 is the Create Rate Card page at its natural
 * 1440 x 1024. It is shown at that width and scaled as one unit, so
 * the product screen keeps its own proportions instead of being
 * relaid out for the slide. */
.atlas-slide--connected-workflows .cw__prev-frame {
  position: absolute;
  top: 152px;
  left: 341px;
  width: 1238px;
  height: 881px;
  overflow: hidden;
  border-radius: 14px;
  background: #FFFFFF;
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.38);
}

.atlas-slide--connected-workflows .cw__prev-mount {
  --rcle-prev-scale: 0.86;
  width: 1440px;
}

/* ============================================================================
 * SLIDE 8: Designing for Connected Pricing Workflows
 *          (.atlas-slide--connected-workflows / .cw__*)
 *
 * Authored 2026-07-08 from Figma node 315:4386. Rebuilt 2026-08-04
 * from the updated Figma node 492:28532 (Rate Card file
 * Klc8JpICWSLwU1LeUVRHtT). Design rationale slide framing the
 * CARD / LINE / PREM three-section pattern as a considered UX
 * decision vs a linear step wizard - not a rejection of the
 * wizard idea. Left column shows three muted grey step cards
 * ("the shape we explored"); right column now shows three
 * dark structured-section cards ("the shape that keeps the
 * workflow connected"). A short purple arrow bridges the two.
 *
 * Right-cards visual system (Figma nodes 513:7979 / 513:7985 /
 * 513:7991): a 2 px vertical highlight bar (#6CB8FF > #1677FF)
 * with a soft blue glow, immediately followed by a 180 x 160
 * card that carries a 41.24 deg gradient (#303946 > #202833 >
 * #273D3D), a 1 px translucent border, a downward drop shadow
 * (0 10 14 rgba(0,0,0,0.32)), and an internal white inner glow
 * (0 1 12 rgba(255,255,255,0.08)). Icons are Phosphor Regular
 * AddressBook / ListNumbers / CurrencyCircleDollar in blue
 * (#2997FF, exported from Figma).
 *
 * All coordinates are Figma-native 1920x1080 pixel values so
 * the slide scales with .atlas-stage's transform: scale() the
 * same way every other HTML slide does. Chrome follows the .rcd__
 * pattern so the deck's bottom strip stays uniform.
 * ========================================================================== */
.atlas-slide--connected-workflows {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: 40px;
  background: #020024;
  font-family: "InspireTWDC", "MultiplaneTWDC Display", "Manrope", "DM Sans", "Open Sans", Inter, sans-serif;
  color: #FFFFFF;
}

.atlas-slide--connected-workflows .cw__stage {
  position: absolute;
  inset: 0;
}

/* -- Header ------------------------------------------------------ */
.atlas-slide--connected-workflows .cw__header {
  position: absolute;
  left: 63px;
  top: 88px;
  width: 1828px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: flex-start;
}
/* text-box-trim: trim-start pulls the top of the title/subtitle onto
 * the cap-line so Manrope/DM Sans fallbacks land at the same
 * y-position as Figma's InspireTWDC:Heavy rendering. Same fix used
 * on slide 5 (.wpc__title / .wpc__subtitle). */
.atlas-slide--connected-workflows .cw__title,
.atlas-slide--connected-workflows .cw__subtitle {
  line-height: 56px;
}
.atlas-slide--connected-workflows .cw__title {
  /* Use the approved InspireTWDC Black face so the title renders in the
   * design system's Black weight rather than the default Heavy that the
   * "MultiplaneTWDC Display" fallback resolves to at font-weight 900.
   * Matches the pattern used on slides 5 / 6 / 9. */
  margin: 0;
  width: 1727px;
  font-family: var(--font-family-inspire);
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  font-size: 56px;
  line-height: 1;
  color: #FFFFFF;
  letter-spacing: -0.01em;
}
.atlas-slide--connected-workflows .cw__subtitle {
  margin: 0;
  width: 1808px;
  font-family: var(--font-family-inspire);
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  font-size: 36px;
  line-height: 56px;
  color: #FFFFFF;
  letter-spacing: -0.005em;
}

/* -- Section heads (Linear Step Wizard / Structured Sections) ----
 * Figma nodes 315:4591 (left) and 315:4592 (right):
 *   - Both are flex-rows, items-center, 16px gap.
 *   - Left icon HandPalm 48x48, right icon CheckCircle 36x36.
 *   - Title text is 32px, InspireTWDC:Medium, leading-56, h-58.
 *   - Left section container (315:4617) is 409px wide at (88, 358).
 *   - Right section container (315:4618) is 335px wide at (960, 358). */
.atlas-slide--connected-workflows .cw__section {
  position: absolute;
  display: flex;
  align-items: center;
  gap: 16px;
  top: 358px;
  height: 58px;
}
.atlas-slide--connected-workflows .cw__section--wizard {
  left: 88px;
  width: 409px;
}
.atlas-slide--connected-workflows .cw__section--structured {
  left: 960px;
  width: 335px;
}
.atlas-slide--connected-workflows .cw__section-icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.atlas-slide--connected-workflows .cw__section-icon--wizard {
  width: 48px;
  height: 48px;
}
.atlas-slide--connected-workflows .cw__section-icon--structured {
  width: 36px;
  height: 36px;
}
.atlas-slide--connected-workflows .cw__section-icon img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: contain;
  -webkit-user-drag: none;
  user-select: none;
}
.atlas-slide--connected-workflows .cw__section-title {
  margin: 0;
  font-weight: 500;
  font-size: 32px;
  line-height: 56px;
  height: 58px;
  white-space: nowrap;
  letter-spacing: -0.005em;
}
.atlas-slide--connected-workflows .cw__section-title--wizard {
  color: #DB5461;
}
.atlas-slide--connected-workflows .cw__section-title--structured {
  color: #6CB288;
}

/* Supporting text under each section head.
 * Figma nodes 492:28551 (left) and 492:28554 (right):
 *   - Left support: 28px InspireTWDC:Medium, leading 56,
 *     rendered inside the flex-column parent at (88, 358) so
 *     it starts at y = 358 + 58 = 416.
 *   - Right support: 28px InspireTWDC:Medium, leading 48,
 *     absolute at (960, 420).
 * We use text-box-trim so the visible cap-top of the glyph lands
 * at the top of the box, matching Figma's absolute y. */
.atlas-slide--connected-workflows .cw__section-support {
  position: absolute;
  margin: 0;
  font-weight: 500;
  font-size: 28px;
  color: #FFFFFF;
  letter-spacing: -0.005em;
}
.atlas-slide--connected-workflows .cw__section-support--wizard {
  left: 88px;
  top: 416px;
  width: 720px;
  line-height: 56px;
}
.atlas-slide--connected-workflows .cw__section-support--structured {
  left: 960px;
  top: 420px;
  width: 900px;
  line-height: 48px;
}

/* -- Left step cards ---------------------------------------------
 * Figma nodes 315:4576 / 315:4569 / 315:4516: each is a 160x137
 * card at y=501, left 88 / 276 / 464. Figma spec is p-[32px] but
 * the icon (72) + gap (8) + label (24) content is 104px tall while
 * the card is only 137 tall, so we use a smaller padding value and
 * rely on justify-content: center to keep the composition centered.
 * That reproduces the effective ~16px vertical inset Figma renders. */
/* 2026-08-05: top moved from 501px to 534px so the left "diagram"
 * (step cards) starts at the exact same y as the right "diagram"
 * (structured cards, top:534px) instead of starting 33px higher.
 * That 33px happens to match the extra height the right description
 * needs to wrap to two lines, so this effectively reserves the same
 * description-height area on the left before either diagram begins. */
.atlas-slide--connected-workflows .cw__step-card {
  position: absolute;
  top: 546px;
  width: 160px;
  height: 137px;
  padding: 16px;
  background: #F8F8F8;
  border: 1.301px solid #E2E3F2;
  border-radius: 10.407px;
  filter: drop-shadow(5.203px 5.203px 0 #51585B);
  opacity: 0.5;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
.atlas-slide--connected-workflows .cw__step-card--1 { left: 88px; }
.atlas-slide--connected-workflows .cw__step-card--2 { left: 276px; }
.atlas-slide--connected-workflows .cw__step-card--3 { left: 464px; }
.atlas-slide--connected-workflows .cw__step-num {
  width: 72px;
  height: 72px;
  display: block;
  flex-shrink: 0;
  object-fit: contain;
}
.atlas-slide--connected-workflows .cw__step-label {
  margin: 0;
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  font-size: 24px;
  line-height: 1;
  color: #595959;
  text-align: center;
  white-space: nowrap;
}

/* -- Middle arrow (purple, thin) ---------------------------------
 * Figma node 29:420 (viewBox 221.5 x 18.525, base slot 255 x 19).
 * 2026-08-05: recentered in the space between the two diagrams
 * instead of the original (697, 603) slot, which sat 33px right of
 * true horizontal center and didn't split the two card sets' vertical
 * centers evenly. Step cards end at x:624 / center y:602.5; structured
 * cards start at x:959 / center y:614 (after the step-card realignment
 * above). Horizontal center of the 624->959 gap is 791.5, so
 * left = 791.5 - 255/2 = 664 (40px clear of both card sets). Vertical
 * center splits the two card-center y's evenly: (602.5+614)/2 = 608.25,
 * so top = 608.25 - 19/2 = 598.75. */
.atlas-slide--connected-workflows .cw__arrow {
  position: absolute;
  left: 697px;
  top: 603px;
  width: 255px;
  height: 19px;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* -- Right structured cards (RC Details / Line Items / Premiums) --
 * Figma nodes 513:7979 (RC Details), 513:7985 (Line Items), and
 * 513:7991 (Premiums). Each card is a horizontal composition of:
 *   - A 2 x 131 px vertical highlight bar (linear-gradient #6CB8FF
 *     -> #1677FF) with a soft blue glow.
 *   - A 180 x 160 dark gradient card (border, drop shadow, inner
 *     glow) containing a 64 px thin blue line icon and a 24 px
 *     InspireTWDC Black label in #F5F7FA.
 * Cards sit at Figma (959, 534), (1277, 534), and (1586, 534). The
 * whole card outer width including the 2 px bar is 182 px. */
.atlas-slide--connected-workflows .cw__struct-card {
  position: absolute;
  top: 537px;
  width: 182px;
  height: 160px;
  display: flex;
  align-items: center;
  gap: 0;
}
/* 2026-08-05: Line Items moved from 1277px to 1272.5px so the two
 * gaps between the three structured cards are equal (131.5px each:
 * RC Details -> Line Items and Line Items -> Premiums) instead of
 * 136px / 127px. RC Details and Premiums keep their original anchors. */
.atlas-slide--connected-workflows .cw__struct-card--rc-details { left: 955px; }
.atlas-slide--connected-workflows .cw__struct-card--line-items  { left: 1277px; }
.atlas-slide--connected-workflows .cw__struct-card--premiums    { left: 1607px; }

.atlas-slide--connected-workflows .cw__struct-bar {
  flex: 0 0 auto;
  width: 2px;
  height: 131px;
  background: linear-gradient(180deg, #6CB8FF 0%, #1677FF 100%);
  box-shadow: 0 0 12px 0 rgba(41, 151, 255, 0.22);
  border-radius: 1px;
}

.atlas-slide--connected-workflows .cw__struct-body {
  position: relative;
  flex: 0 0 auto;
  width: 180px;
  height: 160px;
  padding: 24px 48px;
  border: 1px solid rgba(75, 90, 107, 0.4);
  border-radius: 16px;
  filter: drop-shadow(0 10px 14px rgba(0, 0, 0, 0.32));
  background: linear-gradient(41.24deg, #303946 0.9976%, #202833 12.304%, #27313D 63.185%);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  box-sizing: border-box;
}
/* Inner rim / soft white inner glow per Figma
 * `shadow-[inset_0px_1px_12px_0px_rgba(255,255,255,0.08)]`. */
.atlas-slide--connected-workflows .cw__struct-body::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  pointer-events: none;
  box-shadow: inset 0 1px 12px 0 rgba(255, 255, 255, 0.08);
}

.atlas-slide--connected-workflows .cw__struct-icon {
  display: block;
  width: 64px;
  height: 64px;
  flex-shrink: 0;
  object-fit: contain;
}

.atlas-slide--connected-workflows .cw__struct-label {
  margin: 0;
  font-family: var(--font-family-inspire);
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  font-size: 24px;
  line-height: normal;
  color: #F5F7FA;
  text-align: center;
  white-space: nowrap;
}

/* -- Two-way links between the three connected cards --------------
 * Figma nodes 741:13654 and 741:13656 (Line 1), 121 wide, centred on
 * y = 617 in a box the arrowheads overhang by 5.77px either side.
 *
 * These replaced the old plus signs deliberately. The slide is about
 * being able to go back into any section as a deal changes, not about
 * adding three values together. */
.atlas-slide--connected-workflows .cw__link {
  position: absolute;
  top: 611.23px;
  display: block;
  width: 121px;
  height: 11.54px;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

.atlas-slide--connected-workflows .cw__link--1 { left: 1146px; }
.atlas-slide--connected-workflows .cw__link--2 { left: 1462px; }

/* -- Chrome (cyan brand mark) ------------------------------------ */
.atlas-slide--connected-workflows .cw__brand {
  position: absolute;
  left: 1826px;
  top: 997px;
  width: 36px;
  height: 41px;
  color: #1990B7;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0.95;
}
.atlas-slide--connected-workflows .cw__brand svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* ---------- CTA: invisible click target over Figma's brand mark icon
 *  on the final slide (bottom-right). Position matches the Figma
 *  "Logo" group on node 513:8057: aspect 39/44, anchored at left 95.1% / right
 *  3.02% / top calc(50% + 477.5px) (i.e. centered around y = 1017,
 *  height ~44px so y-range 995..1039). Click area is enlarged to a
 *  comfortable hit target without affecting the visual brand mark
 *  rendered inside the SVG. */
.atlas-slide__cta {
  position: absolute;
  /* Figma frame coords: left ~1825, top ~995, w ~38, h ~44.
   * We give 14px of slop on each side so the hit box is 66x72. */
  left: 1811px;
  top: 981px;
  width: 66px;
  height: 72px;
  padding: 0;
  margin: 0;
  background: transparent;
  border: 0;
  border-radius: 14px;
  cursor: pointer;
  pointer-events: auto;
  /* Premium hover affordance: subtle translucent halo behind the brand
   * mark, plus a small floating tooltip caption that fades in. */
  transition: background-color 180ms ease, transform 180ms ease,
              box-shadow 180ms ease;
}
.atlas-slide__cta:hover {
  background-color: rgba(255, 255, 255, 0.06);
  box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.04);
  transform: translateY(-1px);
}
.atlas-slide__cta:active { transform: translateY(0); }
.atlas-slide__cta:focus-visible {
  outline: 2px solid #FFFFFF;
  outline-offset: 3px;
}
.atlas-slide__cta-tip {
  position: absolute;
  right: 0;
  bottom: calc(100% + 8px);
  white-space: nowrap;
  padding: 8px 12px;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.92);
  color: #020024;
  font: 600 18px/1 "Open Sans", "DM Sans", sans-serif;
  letter-spacing: 0.01em;
  opacity: 0;
  pointer-events: none;
  transform: translateY(4px);
  transition: opacity 180ms ease, transform 180ms ease;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
}
.atlas-slide__cta:hover .atlas-slide__cta-tip,
.atlas-slide__cta:focus-visible .atlas-slide__cta-tip {
  opacity: 1;
  transform: translateY(0);
}

/* ============================================================================
 * SLIDE 9: Rate Card Powers Line Pricing
 * Figma node 513:8052 on the fixed 1920 x 1080 Atlas canvas.
 * ========================================================================== */
.atlas-slide--line-pricing .rlp {
  --rlp-background: #020024;
  --rlp-text: #F5F5F7;
  --rlp-text-muted: #D2D2D7;
  --rlp-rcm: #FF5A43;
  --rlp-rcm-border: #FF7666;
  --rlp-planning: #2097F6;
  --rlp-connector: #5452F6;
  --rlp-card-gradient: linear-gradient(
    34.466320773824165deg,
    rgb(48, 57, 70) 0.9976%,
    rgb(32, 40, 51) 12.304%,
    rgb(39, 49, 61) 63.185%
  );

  position: relative;
  width: 1920px;
  height: 1080px;
  overflow: hidden;
  border-radius: 40px;
  background: var(--rlp-background);
  color: var(--rlp-text);
  font-family: var(--font-family-inspire);
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
}

.atlas-slide--line-pricing .rlp__header {
  position: absolute;
  left: 63px;
  top: 88px;
  width: 1828px;
}

.atlas-slide--line-pricing .rlp__title,
.atlas-slide--line-pricing .rlp__subtitle,
.atlas-slide--line-pricing .rlp__card-title,
.atlas-slide--line-pricing .rlp__card-support,
.atlas-slide--line-pricing .rlp__statement {
  margin: 0;
}

/* Restored to the Heavy face this slide's Figma authoring specifies (the
 * .rlp parent cascades Heavy for exactly that reason). A 2026-08-05 change
 * had overridden title and subtitle to Black to give several slides one
 * shared title treatment; that was a deviation from the source. */
.atlas-slide--line-pricing .rlp__title {
  width: 1727px;
  color: var(--rlp-text);
  font-family: var(--font-family-inspire);
  font-size: 56px;
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
  line-height: 56px;
  white-space: nowrap;
}

/* 1200px (not the full 1808px header width) is a deliberate text-box
 * width: at the full width this 110-char sentence would wrap with
 * "workflows." orphaned alone on line 2. At 1200px it breaks after
 * "use" into a 9-word / 6-word pair that reads as two balanced
 * clauses instead of one long line plus a stray word. */
.atlas-slide--line-pricing .rlp__subtitle {
  width: 1200px;
  margin-top: 8px;
  color: var(--rlp-text-muted);
  font-family: var(--font-family-inspire);
  font-size: 36px;
  font-weight: var(--font-subheading); /* InspireTWDC Heavy 600 */
  line-height: 56px;
  /* 2026-08-05: nowrap forced this 110-char sentence past the 1808px
   * box (and past the 1920px stage edge) with several fallback
   * fonts. Switched to natural wrapping, matching .cw__subtitle
   * (slide 7); the RCM/Core Planning cards sit at top:353px, far
   * enough down that a second line never collides with them. */
  white-space: normal;
}

.atlas-slide--line-pricing .rlp__card {
  position: absolute;
  box-sizing: border-box;
  width: 500px;
  height: 313px;
  overflow: hidden;
  border-style: solid;
  border-radius: 25.143px;
  background-image: var(--rlp-card-gradient);
}

/* 2026-08-06: bullet-row refresh shortened both cards from 348px to
 * 313px (10% reduction) now that the filled CARD/LINE/PREM and
 * Context/Base Rate/Adjustments pills are gone. Both cards keep the
 * same top (370.5px) so their top/bottom edges and shared
 * centerline (370.5 + 313/2 = 527px) still line up exactly. */
.atlas-slide--line-pricing .rlp__card--rcm {
  left: 63px;
  top: 370.5px;
  border-width: 1px;
  border-color: var(--rlp-rcm-border);
}

.atlas-slide--line-pricing .rlp__card--planning {
  left: 823px;
  top: 370.5px;
  border-width: 2.2px;
  border-color: var(--rlp-planning);
}

.atlas-slide--line-pricing .rlp__card-header {
  position: absolute;
  left: 35.2px;
  top: 26.4px;
  width: 471.9px;
}

.atlas-slide--line-pricing .rlp__heading-row {
  display: flex;
  align-items: center;
  gap: 8.8px;
  height: 52.8px;
}

.atlas-slide--line-pricing .rlp__heading-icon {
  display: block;
  flex: 0 0 auto;
  object-fit: fill;
  user-select: none;
  -webkit-user-drag: none;
}

.atlas-slide--line-pricing .rlp__heading-icon--rcm {
  width: 54.6794px;
  height: 52.8px;
}

.atlas-slide--line-pricing .rlp__heading-icon--planning {
  width: 52.8px;
  height: 52.8px;
}

.atlas-slide--line-pricing .rlp__card-title {
  font-size: 36px;
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600, inherited before */
  line-height: normal;
  text-align: center;
  white-space: nowrap;
}

.atlas-slide--line-pricing .rlp__card--rcm .rlp__card-title {
  color: var(--rlp-rcm);
}

.atlas-slide--line-pricing .rlp__card--planning .rlp__card-title {
  color: var(--rlp-planning);
}

/* 2026-08-06: left-aligned to the same guide as the row copy below
 * (the "RCM" / "Core Planning" title's own left edge, i.e. the
 * heading icon width + the 8.8px icon/title gap) instead of being
 * centered under the icon+title group. See .rlp__rows--rcm /
 * .rlp__rows--planning for the matching row-copy guide. */
.atlas-slide--line-pricing .rlp__card-support {
  height: 31.9px;
  margin-top: 4.4px;
  color: var(--rlp-text);
  font-family: var(--font-family-inspire);
  font-size: 26.4px;
  font-weight: var(--font-body); /* InspireTWDC Medium 500 */
  line-height: normal;
  text-align: left;
  white-space: nowrap;
}

.atlas-slide--line-pricing .rlp__card--rcm .rlp__card-support {
  margin-left: 63.4794px; /* rcm heading icon (54.6794px) + gap (8.8px) */
}

.atlas-slide--line-pricing .rlp__card--planning .rlp__card-support {
  margin-left: 61.6px; /* planning heading icon (52.8px) + gap (8.8px) */
}

.atlas-slide--line-pricing .rlp__rows {
  position: absolute;
  display: grid;
  row-gap: 16px;
}

/* 2026-08-06: left edge moved from the shared 35.2px card gutter to
 * each card's own title-text guide (heading icon width + 8.8px
 * icon/title gap), so the row copy lines up with "RCM" / "Core
 * Planning" instead of the icon. .rlp__card-support uses the same
 * guide via matching margin-left values. */
.atlas-slide--line-pricing .rlp__rows--rcm {
  left: 98.6794px;
  top: 156px;
  width: 408.4206px; /* 471.9px shared row width, less the guide inset */
}

.atlas-slide--line-pricing .rlp__rows--planning {
  left: 96.8px;
  top: 156px;
  width: 410.3px; /* 471.9px shared row width, less the guide inset */
}

/* 2026-08-06: the filled CARD/LINE/PREM and Context/Base Rate/
 * Adjustments term pills are gone entirely (bullet + explanation
 * only, per the neutral-bullet-list brief). The bullet is taken out
 * of flow and hung slightly left of the row's own edge so every
 * row-copy line starts flush on the shared title guide; .rlp__row
 * establishes the positioning context for that hanging bullet. */
.atlas-slide--line-pricing .rlp__row {
  position: relative;
  display: flex;
  align-items: baseline;
  min-height: 28px;
}

/* Neutral bullet, identical in both cards (no more coral/blue
 * per-card tint) so the list reads as informational, not a repeat
 * of the card's brand accent. */
.atlas-slide--line-pricing .rlp__bullet {
  position: absolute;
  left: -16px;
  top: 50%;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--rlp-text-muted);
  transform: translateY(-50%);
}

.atlas-slide--line-pricing .rlp__row-copy {
  flex: 1 1 auto;
  color: var(--rlp-text-muted);
  font-size: 22px;
  font-weight: 500;
  line-height: 26.4px;
  text-align: left;
  white-space: nowrap;
}

/* 2026-08-04 copy refresh: the block now holds a primary
 * "Applicable rate" label plus a small "via shared pricing
 * capabilities" subline above the arrow, so the container is
 * a little taller and its baseline is pulled up slightly so
 * the arrow keeps landing between the two cards.
 * 2026-08-05: left nudged from 579px to 581.3px so the connector
 * line/arrow (9px inset, 205.4px wide) sits exactly centered in the
 * 260px gap between the RCM and Core Planning cards (563px -> 823px)
 * instead of 2-3px off center. */
.atlas-slide--line-pricing .rlp__published {
  position: absolute;
  left: 581.3px;
  top: 452px;
  width: 276px;
  height: 92px;
}

/* 2026-08-05: switched from left-aligned text in a 260px box to text
 * centered in a box that matches the connector line/arrow's own
 * 205.4px width and 9px inset, so "Applicable rate" sits centered
 * above the line instead of left-aligned with the line's start. */
.atlas-slide--line-pricing .rlp__connector-label {
  display: block;
  margin-left: 9px;
  width: 205.4px;
  color: var(--rlp-text);
  font-size: 24px;
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600, inherited before */
  line-height: 30px;
  text-align: center;
  white-space: nowrap;
}

.atlas-slide--line-pricing .rlp__connector-sublabel {
  display: block;
  margin-top: 4px;
  margin-left: 9px;
  width: 205.4px;
  color: rgba(245, 247, 250, 0.6);
  font-family: "Open Sans", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Helvetica Neue", sans-serif;
  font-size: 16px;
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: 0.005em;
  text-align: center;
  white-space: nowrap;
}

.atlas-slide--line-pricing .rlp__connector {
  position: absolute;
  left: 9px;
  top: 72px;
  width: 205.4px;
  height: 20px;
}

.atlas-slide--line-pricing .rlp__connector-dot,
.atlas-slide--line-pricing .rlp__connector-line,
.atlas-slide--line-pricing .rlp__connector-head {
  position: absolute;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}

.atlas-slide--line-pricing .rlp__connector-dot {
  left: 0;
  top: 4.95px;
  width: 12.1px;
  height: 12.1px;
}

.atlas-slide--line-pricing .rlp__connector-line {
  left: 11px;
  top: 8.9px;
  width: 184.5px;
  height: 2.2px;
}

.atlas-slide--line-pricing .rlp__connector-head {
  left: 194.4px;
  top: 1.15px;
  width: 10.987px;
  height: 17.7071px;
}

/* 2026-08-05: the previous 26px size measured 526px wide for "Core
 * Planning will use the applicable rate." (the longer of the two
 * lines) inside a 505px box left at 1400px, i.e. the real text
 * overran its own box by 21px and reached x=1926, 6px past the
 * 1920px stage (and therefore clipped by .rlp's overflow:hidden).
 * Dropping to 24px (proportionally tighter 40px leading) brings the
 * longer line down to ~486px; the whole block is re-anchored so its
 * right edge lands at 1900px (a clean 20px clear of the stage edge)
 * and is vertically centered on the RCM/Core Planning cards'
 * shared centerline (353 + 348/2 = 527px). */
.atlas-slide--line-pricing .rlp__statement {
  position: absolute;
  left: 1390px;
  top: 487px;
  width: 510px;
  height: 80px;
  color: var(--rlp-text);
  font-size: 24px;
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600, inherited before */
  line-height: 40px;
  white-space: nowrap;
}

/* 2026-08-05: repositioned so it sits exactly midway between the
 * Core Planning card's right edge (1323px) and the conclusion text's
 * left edge (1390px), with equal ~13.7px clearance on each side
 * instead of the ~16px gap it happened to land in before, and
 * recentered vertically on the same 527px centerline as the equals
 * sign's surrounding diagram. */
.atlas-slide--line-pricing .rlp__equals {
  position: absolute;
  left: 1336.7px;
  top: 518.75px;
  display: block;
  width: 39.6px;
  height: 16.5px;
  user-select: none;
  -webkit-user-drag: none;
}

.atlas-slide--line-pricing .rlp__brand {
  position: absolute;
  left: 1826px;
  top: 997px;
  display: block;
  width: 36px;
  height: 41px;
  user-select: none;
  -webkit-user-drag: none;
}

/* ---------- Navigation: NONE -----------------------------------------
 *  The Atlas deck has no on-screen navigation chrome. The full deck
 *  area is the click target (handler bound to .atlas-stage-wrap in
 *  initAtlasDeck). Keyboard nav (Left / Right / Space / PageUp /
 *  PageDown / Home / End / Esc) is also wired there. The only
 *  visible interactive affordance is the invisible
 *  [data-atlas-enter] CTA over the Figma brand mark on the final
 *  slide (slide 8), which still uses pointer-events: auto to opt
 *  back in.
 *  ------------------------------------------------------------------ */

/* ============================================================================
 * SLIDE 5: Turning Rate Cards into Structured Pricing Data
 *
 * Rebuilt from Figma node 492:28839 (Rate Card file,
 * Klc8JpICWSLwU1LeUVRHtT). Uses the side-by-side spreadsheet contrast:
 * a busy human-authored Excel rate card on the left, the same data
 * normalized into CARD / LINE rows on the right, and an indigo arrow
 * between them showing the transformation RCM performs.
 *
 * Coordinates (Figma node 492:28839 on the 1920 x 1080 canvas):
 *   Header frame          (63, 88)     1828 x 120
 *   Current Rate Card     (75, 325)    777 x 487   [Figma 286:4400]
 *   Structured Rate Data  (985, 325)   877 x 487   [Figma 286:4390]
 *   Arrow group           (865, 578)   105.5 x 18.525
 *   Current label         (310, 824)   307 wide
 *   Structured label      (1207, 824)  433 wide
 *   Disney D mark         (1826, 997)  36 x 41
 *
 * The two screenshots ship as lossless, pre-cropped PNGs.
 * The raw Figma image assets are full-screen Teams recordings that
 * include meeting participants, a chat panel, and an internal Jira
 * URL; Figma hides those via overflow-hidden but the underlying
 * bytes would still be reachable from DevTools. Crop is done at
 * author time to the exact rectangle Figma displays so nothing
 * private lands in the browser DOM.
 * ========================================================================== */

.atlas-slide--rate-card-data .rcd {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: 40px;
  background: #020024;
  color: #FFFFFF;
  font-family: "InspireTWDC", "MultiplaneTWDC Display", "Open Sans", sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: geometricPrecision;
}

/* Header. Figma 286:4262 anchors the frame at (63, 88), 1828 x 120,
 * title on top and subtitle 64px below. Both use InspireTWDC Heavy;
 * subtitle sits at 36px per Figma (the deck's other subtitles use
 * 32px medium — this slide's Figma authoring is denser). */
.atlas-slide--rate-card-data .rcd__header {
  position: absolute;
  left: 63px;
  top: 88px;
  width: 1828px;
  height: 120px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* Restored to the Figma 286:4262 spec recorded above: both header lines are
 * InspireTWDC Heavy, the title at 56px and the subtitle at 36px. A 2026-08-05
 * change had overridden both to the Black face to give four slides a single
 * title treatment; that was a deviation from the source, and combined with
 * font-weight: 900 against a single-Regular family it also forced synthetic
 * bold, which is why the subtitle stopped reading as secondary. */
.atlas-slide--rate-card-data .rcd__title {
  width: 1727px;
  margin: 0;
  font-family: var(--font-family-inspire);
  font-weight: var(--font-heading);
  font-size: 56px;
  line-height: 56px;
  letter-spacing: 0;
  color: #FFFFFF;
  white-space: nowrap;
}
.atlas-slide--rate-card-data .rcd__subtitle {
  /* 1300px (not the full 1808px header width) is a deliberate text-box
   * width: at the full width this 114-char sentence would wrap with
   * "workflows." orphaned alone on line 2. At 1300px it breaks after
   * "for" into an 11-word / 5-word pair that reads as two balanced
   * phrases instead of one long line plus a stray word. */
  width: 1300px;
  margin: 0;
  font-family: var(--font-family-inspire);
  font-weight: var(--font-subheading);
  font-size: 36px;
  line-height: 56px;
  color: #FFFFFF;
  /* 2026-08-05: nowrap forced this 114-char sentence past the 1808px
   * box (and past the 1920px stage edge) with several fallback
   * fonts. Switched to natural wrapping, matching the .cw__subtitle
   * pattern (slide 7); the image row below sits at top:325px, far
   * enough down that a second line never collides with it. */
  white-space: normal;
}

/* Screenshot slots. Figma scales the raw 1804x1128 asset to 140.97%
 * / 140.65% (image 3) or 140.72% / 158.43% (image 1) of the slot,
 * then clips via overflow-hidden. The shipping JPGs are already
 * cropped at author time to the exact visible rectangle and match
 * the container aspect ratio (777:487 for image 3, 877:487 for
 * image 1), so object-fit:cover lands them 1:1 without further
 * scaling. Both slots keep the Figma-authored 487px height and top,
 * so the two screenshots already share identical top/bottom edges;
 * only the shared left offset moved (75px -> 66.5px) so the combined
 * two-column group (75px -> 1862px, width 1787px) sits centered on
 * the canvas midpoint (960px) instead of 8.5px right of it. */
.atlas-slide--rate-card-data .rcd__image {
  position: absolute;
  top: 325px;
  height: 487px;
  overflow: hidden;
  background: #FFFFFF;
}
.atlas-slide--rate-card-data .rcd__image--current {
  left: 66.5px;
  width: 777px;
}
.atlas-slide--rate-card-data .rcd__image--structured {
  left: 976.5px;
  width: 877px;
}
.atlas-slide--rate-card-data .rcd__image img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  image-rendering: -webkit-optimize-contrast;
  user-select: none;
  -webkit-user-drag: none;
}

/* Exact three-part Figma connector export. Left offset shifted with
 * the two screenshots above (-8.5px) so it stays centered in the
 * gutter between them. */
.atlas-slide--rate-card-data .rcd__arrow {
  position: absolute;
  left: 856.5px;
  top: 578px;
  display: block;
  width: 105.5px;
  height: 18.525px;
}
.atlas-slide--rate-card-data .rcd__arrow img {
  position: absolute;
  display: block;
  max-width: none;
  pointer-events: none;
}
.atlas-slide--rate-card-data .rcd__arrow-circle {
  left: 0;
  top: 3.5px;
  width: 11px;
  height: 11px;
}
.atlas-slide--rate-card-data .rcd__arrow-line {
  left: 10px;
  top: 8.237px;
  width: 94px;
  height: 1.76249px;
}
.atlas-slide--rate-card-data .rcd__arrow-head {
  left: 95px;
  top: 1.25px;
  width: 9.98821px;
  height: 16.0974px;
}

/* Screenshot labels below each image. Figma 295:4419 / 295:4420 use
 * 36px InspireTWDC Heavy with 56px leading, centered horizontally
 * under the corresponding screenshot. */
.atlas-slide--rate-card-data .rcd__label {
  position: absolute;
  top: 824px;
  margin: 0;
  font-family: var(--font-family-inspire);
  font-weight: var(--font-subheading);
  font-size: 36px;
  line-height: 56px;
  color: #FFFFFF;
  text-align: center;
  white-space: nowrap;
}
/* 2026-08-05: both captions now use one identical 520px box (wide
 * enough to hold the longer "Existing Rate Card Workbook" string on
 * one line with room to spare) centered on its own screenshot's
 * horizontal midpoint, instead of two different bespoke widths sized
 * to each string. Current image center = 66.5 + 777/2 = 455;
 * Structured image center = 976.5 + 877/2 = 1415. */
.atlas-slide--rate-card-data .rcd__label--current {
  left: 195px;
  width: 520px;
}
.atlas-slide--rate-card-data .rcd__label--structured {
  left: 1155px;
  width: 520px;
}
/* Arrow caption between the two workbooks (2026-08-04 brief).
 * The gap between the two image tiles is only 133px wide (Current
 * ends at 843.5px, Structured begins at 976.5px), so the caption is
 * rendered as a stacked two-line label ("Import" / "and structure")
 * centered on the arrow's horizontal midpoint (~908.5px) and sitting
 * directly above the flow arrow at top:578px. Uses body typography
 * (Open Sans) at 18px so it reads as an in-flow label without
 * competing with the 36px image captions below. */
.atlas-slide--rate-card-data .rcd__label--arrow {
  top: 512px;
  left: 843.5px;
  width: 132px;
  font-family: 'Open Sans', system-ui, sans-serif;
  font-weight: 600;
  font-size: 16px;
  line-height: 22px;
  color: rgba(255, 255, 255, 0.82);
  letter-spacing: 0;
  text-transform: none;
  text-align: center;
}
.atlas-slide--rate-card-data .rcd__label--arrow span {
  display: block;
}
.atlas-slide--rate-card-data .rcd__brand {
  position: absolute;
  left: 1826px;
  top: 997px;
  width: 36px;
  height: 41px;
  display: block;
  max-width: none;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* ============================================================================
 * SLIDE 7: Why Pricing Gets Complicated Before Planning
 *          (.atlas-slide--why-pricing / .wpc)
 *
 * Restored 2026-07-08 to Figma node 282:5389 (Rate Card file
 * Klc8JpICWSLwU1LeUVRHtT) pixel-for-pixel.
 *
 * The layout is a 3 x 3 grid of pricing input categories on the left
 * (each: 232 x 180 dark-navy card with a 48 x 48 coral outline icon
 * centered on top, bold InspireTWDC Black label, and a muted 22px
 * Roman list of the concrete inputs) that feeds two thin indigo
 * arrows into a coral "RCM" window-card and then a blue "Core
 * Planning" window-card. The window cards are the exact Figma
 * "Component/ Windows/ Orange" / "Component/ Windows/ Blue" pattern:
 *   - Light shade top strip (~34.3px, 3 small dots on the left)
 *     with a 1.43px border in the accent color, a top-only 11.4px
 *     radius, and a 5.7px/5.7px offset shade shadow.
 *   - White body (~146.5px) with the same accent border, bottom-only
 *     11.4px radius, same offset shadow, holding an icon and the
 *     RCM / Core Planning label in the accent color.
 *   - Outer drop-shadow glow (blue for RCM, subtle purple for CP).
 *
 *   Header block         (63,   88)    1828 x 120
 *   Card row 1 y = 283   (Buyer 63,  Deal Context 311, Product 559)
 *   Card row 2 y = 494   (Base Rate 63, Premiums 313, Discount 559)
 *   Card row 3 y = 708   (Targeting 63, Timing 313, Downstream 559)
 *   Arrow 1              (843,  577)   151.5 x 18.525
 *   RCM window-card      (1035, 496)   216   x 180.238
 *   RCM caption          (1007, 705)   271 wide, centered on 1142.5
 *   Arrow 2              (1290, 577)   151.5 x 18.525
 *   Core Planning window (1480, 496)   216   x 180.238
 *   Core Planning cap.   (1447, 708)   282 wide
 *   Footer text          (60,   1010)  404 x 16   (deck consistency)
 *   Ad Platforms mark    (1826, 997)   36 x 41
 *
 * Palette (Figma "EDL System Palette"):
 *   Coral:   #FF5A43   /  Shade coral: #FFEEEA (top) + #FCBCB3 (offset)
 *   Blue:    #2097F6   /  Shade blue:  #E7F4FF (top) + #83C4F8 (offset)
 *   Card bg: rgba(21, 22, 47, 0.72) over the slide navy
 *   Border:  rgba(124, 126, 255, 0.30)  (indigo 30%)
 *   Arrow:   #6567CE   (indigo, reused from .rcd arrow language)
 *
 * All .wpc__* rules are scoped to .atlas-slide--why-pricing.
 * ========================================================================== */
.atlas-slide--why-pricing .wpc {
  position: absolute;
  inset: 0;
  color: #FFFFFF;
  font-family: "InspireTWDC", "MultiplaneTWDC Display", "Manrope", "DM Sans", "Open Sans", Inter, sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: geometricPrecision;

  --wpc-input-accent: #FF5A43;
  --wpc-cp-accent:    #2097F6;
}

/* --- Header. Anchor + typography match .rcd so the deck's
 *     content slides feel like one system. ----------------------- */
.atlas-slide--why-pricing .wpc__header {
  /* Figma frame origin: (63, 88). InspireTWDC:Heavy in Figma
   * renders with almost no leading above cap-height, so its
   * top-of-box sits ~10px higher than Manrope / DM Sans, which
   * we use as a public-font fallback. text-box-trim pulls the
   * top edge of the title down onto the cap line so the visible
   * glyphs land at the same y as Figma's node 282:5392. */
  position: absolute;
  left: 63px;
  top: 88px;
  width: 1828px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.atlas-slide--why-pricing .wpc__title,
.atlas-slide--why-pricing .wpc__subtitle {
  text-box-trim: trim-start;
  text-box-edge: cap alphabetic;
}
.atlas-slide--why-pricing .wpc__title {
  margin: 0;
  font-weight: 900;
  font-size: 56px;
  line-height: 1;
  letter-spacing: -0.005em;
  color: #FFFFFF;
}
.atlas-slide--why-pricing .wpc__subtitle {
  margin: 0;
  font-weight: 900;
  font-size: 36px;
  line-height: 44px;
  color: #FFFFFF;
}

/* --- Input card. 232 x 180 with a 2px indigo border, 18px radius,
 *     dark tint over the slide, and the Figma dual shadow (outer
 *     drop + subtle blue-indigo glow). Content is centered: 48px
 *     outline icon on top, 24px InspireTWDC Black label, 22px
 *     InspireTWDC Roman description. Cards are absolutely
 *     positioned via inline left/top to match Figma coords exactly. */
.atlas-slide--why-pricing .wpc__card {
  /* Figma node 282:5455 (Buyer) and siblings. Frame is 232 x 180,
   * padded 24 top / 4 sides / 4 bottom, flex column, centered on
   * both axes, with an 8px gap between the icon and the text
   * block. The text block itself is a nested flex with 16px gap
   * (label -> sub) achieved here via margin-top on .wpc__sub so
   * the flat DOM stays simple. */
  position: absolute;
  width: 232px;
  height: 180px;
  padding: 24px 4px 4px;
  border: 2px solid rgba(124, 126, 255, 0.30);
  border-radius: 18px;
  background:
    linear-gradient(rgba(21, 22, 47, 0.72), rgba(21, 22, 47, 0.72)),
    rgba(255, 255, 255, 0);
  box-shadow:
    0 8px 24px rgba(0, 0, 0, 0.35),
    0 0 24px rgba(84, 92, 255, 0.18),
    inset 0 1px 0 rgba(255, 255, 255, 0.08);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  overflow: hidden;
}
.atlas-slide--why-pricing .wpc__icon {
  flex: 0 0 auto;
  width: 48px;
  height: 48px;
  color: var(--wpc-input-accent);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.atlas-slide--why-pricing .wpc__icon svg,
.atlas-slide--why-pricing .wpc__icon img {
  /* Icons are the exact Figma vector exports (Phosphor family) for
   * node 282:5389, downloaded on 2026-07-08 as ./assets/atlas/
   * wpc-*.svg. Their intrinsic viewBoxes are wider or taller than
   * 48x48 (Figma extends each icon slightly past its 48px slot per
   * per-icon negative-inset math in the node). object-fit: contain
   * keeps each glyph inside 48x48 without distortion; the Figma
   * treatment is preserved because these icons are the exact
   * assets, not redraws. */
  width: 48px;
  height: 48px;
  display: block;
  object-fit: contain;
  -webkit-user-drag: none;
  user-select: none;
}
.atlas-slide--why-pricing .wpc__label {
  /* Figma spec: text-[24px] font-['InspireTWDC:Black'] h-[24px]. */
  margin: 0;
  font-weight: 900;
  font-size: 24px;
  line-height: 1;
  color: #FFFFFF;
  text-align: center;
  letter-spacing: -0.005em;
}
.atlas-slide--why-pricing .wpc__sub {
  /* Figma spec: text-[22px] font-['InspireTWDC:Roman'] w-[228px]
   * leading-[normal] (~1.2). 8px margin-top on top of the 8px flex
   * gap gives the 16px label -> sub spacing Figma uses via its
   * nested content-stretch flex column. */
  margin: 8px 0 0;
  font-weight: 400;
  font-size: 22px;
  line-height: 1.18;
  color: #FFFFFF;
  text-align: center;
  width: 228px;
  max-width: 100%;
}

/* --- Arrows. 151.5 x 18.5 indigo shaft with a dot at the start and
 *     an arrowhead at the tip. Same asset used twice, matching the
 *     Figma Group627386 node. -------------------------------------- */
.atlas-slide--why-pricing .wpc__arrow {
  position: absolute;
  width: 151.5px;
  height: 18.525px;
}
.atlas-slide--why-pricing .wpc__arrow svg,
.atlas-slide--why-pricing .wpc__arrow img {
  width: 100%;
  height: 100%;
  display: block;
  -webkit-user-drag: none;
  user-select: none;
}

/* --- RCM + Core Planning window-cards. Two absolutely-stacked
 *     sub-blocks (top strip + white body) sharing a common outer
 *     drop-shadow. Sizes are Figma-native px so the composition
 *     matches node 282:5389 exactly. ----------------------------- */
.atlas-slide--why-pricing .wpc__window {
  position: absolute;
  width: 216px;
  height: 180.238px;
}
.atlas-slide--why-pricing .wpc__window--rcm {
  filter: drop-shadow(0 4.8px 28.8px rgba(47, 128, 255, 0.55));
}
.atlas-slide--why-pricing .wpc__window--cp {
  filter: drop-shadow(0 0 12px rgba(111, 140, 255, 0.28));
}

/* Top strip: light-shade band with three small dots on the left. */
.atlas-slide--why-pricing .wpc__window-strip {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  height: 34.34px;
  padding: 11.44px;
  border: 1.43px solid;
  border-top-left-radius: 11.444px;
  border-top-right-radius: 11.444px;
  display: flex;
  align-items: center;
  gap: 8px;
  box-sizing: border-box;
}
.atlas-slide--why-pricing .wpc__window--rcm .wpc__window-strip {
  background: #FFEEEA;
  border-color: #FF5A43;
  box-shadow: 5.72px 5.72px 0 0 #FCBCB3;
}
.atlas-slide--why-pricing .wpc__window--cp .wpc__window-strip {
  background: #E7F4FF;
  border-color: #2097F6;
  box-shadow: 5.72px 5.72px 0 0 #83C4F8;
}
.atlas-slide--why-pricing .wpc__window-dot {
  width: 10.29px;
  height: 10.29px;
  border-radius: 999px;
}
.atlas-slide--why-pricing .wpc__window--rcm .wpc__window-dot { background: #FF5A43; }
.atlas-slide--why-pricing .wpc__window--cp  .wpc__window-dot { background: #2097F6; }

/* Body: white card that holds the icon + accent label. Positioned
 * with a small overlap onto the top strip (inset 18.47% / 0.22%
 * from Figma) so both borders read as one continuous window shape. */
.atlas-slide--why-pricing .wpc__window-body {
  position: absolute;
  left: 0;
  right: 0;
  top: 18.47%;
  bottom: 0.22%;
  padding: 19.2px 8px 20px;
  background: #FFFFFF;
  border: 1.43px solid;
  border-bottom-left-radius: 11.444px;
  border-bottom-right-radius: 11.444px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 9.6px;
  box-sizing: border-box;
}
.atlas-slide--why-pricing .wpc__window--rcm .wpc__window-body {
  border-color: #FF5A43;
  box-shadow: 5.72px 5.72px 0 0 #FCBCB3;
}
.atlas-slide--why-pricing .wpc__window--cp .wpc__window-body {
  border-color: #2097F6;
  box-shadow: 5.72px 5.72px 0 0 #83C4F8;
}
.atlas-slide--why-pricing .wpc__window-glyph {
  display: block;
  flex: 0 0 auto;
}
.atlas-slide--why-pricing .wpc__window-label {
  margin: 0;
  font-weight: 900;
  font-size: 22.887px;
  line-height: 1.05;
  text-align: center;
  white-space: nowrap;
  letter-spacing: -0.005em;
}
.atlas-slide--why-pricing .wpc__window--rcm .wpc__window-label { color: #FF5A43; }
.atlas-slide--why-pricing .wpc__window--cp  .wpc__window-label { color: #2097F6; }

/* --- Captions under the RCM and Core Planning window-cards.
 *     Positioned via inline left/top on each .wpc__caption node. --- */
.atlas-slide--why-pricing .wpc__caption {
  position: absolute;
  margin: 0;
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  font-size: 24px;
  line-height: 1.2;
  color: #FFFFFF;
  text-align: center;
  white-space: nowrap;
}

/* --- Footer + Disney "D" brand mark. Same anchor and typography as
 *     .rcd__footer so the deck's chrome stays uniform. */
.atlas-slide--why-pricing .wpc__footer {
  position: absolute;
  left: 60px;
  top: 1010px;
  margin: 0;
  font-family: "SF Pro Text", "SF Pro", -apple-system, BlinkMacSystemFont, "Open Sans", sans-serif;
  font-weight: 600;
  font-size: 16px;
  line-height: 1;
  letter-spacing: -0.01em;
  color: rgba(255, 255, 255, 0.4);
  white-space: nowrap;
}
.atlas-slide--why-pricing .wpc__brand {
  position: absolute;
  left: 1826px;
  top: 997px;
  width: 36px;
  height: 41px;
  color: #1990B7;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0.95;
}
.atlas-slide--why-pricing .wpc__brand svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* Latest Slide 6 design, Figma 492:28715 and modal 492:28861. */
.atlas-slide--why-pricing .wpc {
  --wpc-background: #020024;
  --wpc-card-background: rgba(21, 22, 47, 0.72);
  --wpc-card-border: rgba(124, 126, 255, 0.30);
  --wpc-card-active: rgba(255, 118, 102, 0.62);
  --wpc-card-icon: #FF7666;
  --wpc-flow-surface: #202735;
  --wpc-flow-border: #384252;
  --wpc-flow-gold: #D6A85F;
  --wpc-flow-muted: #AEB5C5;
  --wpc-connector: #7C7EFF;
  background: var(--wpc-background);
}

.atlas-slide--why-pricing .wpc__title {
  width: 1727px;
  /* Approved InspireTWDC Black face, not the default Heavy fallback. */
  font-family: var(--font-family-inspire);
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  letter-spacing: 0;
  white-space: nowrap;
}

.atlas-slide--why-pricing .wpc__subtitle {
  width: 1808px;
  font-family: var(--font-family-inspire);
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
  line-height: 56px;
  white-space: nowrap;
}

.atlas-slide--why-pricing .wpc__card {
  z-index: 2;
  box-sizing: border-box;
  width: 252px;
  height: 189px;
  padding: 0;
  appearance: none;
  border: 2px solid var(--wpc-card-border);
  border-radius: 18px;
  background: var(--wpc-card-background);
  color: #FFFFFF;
  font: inherit;
  text-align: center;
  cursor: pointer;
  pointer-events: auto;
  transition:
    border-color 140ms ease,
    box-shadow 140ms ease,
    transform 140ms ease,
    background-color 140ms ease;
}

/* 2026-08-12: the selected card gets a stacking boost so it stays
 * fully visible (not covered by the dimmed scrim) while its popover
 * is open, matching the requested back-to-front order: connectors
 * (1) -> cards/windows (2) -> dimmed overlay (3) -> selected card (4)
 * -> popover (5, see .wpc-modal__scrim / .wpc-modal__panel below). */
.atlas-slide--why-pricing .wpc__card.is-active {
  z-index: 4;
}

body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"])
  .atlas-slide--why-pricing .wpc__card {
  border-radius: 18px;
}

/* 2026-08-06: cards enlarged 232x180 -> 252x189 (+8.6% w / +5% h) so
 * the longer PRD-accurate copy fits without clipping or a 3rd wrapped
 * line. Column gap stays 16px (col2 = 63 + 252 + 16 = 331; col3 =
 * 331 + 252 + 16 = 599). Row gap stays 31px; the 2 x 3 grid is
 * 409px tall (189*2 + 31) and stays centered on the workflow group's
 * shared centerline (y=581, see .wpc__window--rcm / --cp below):
 * row 1 top = 581 - 409/2 = 376.5, row 2 top = 376.5 + 189 + 31 =
 * 596.5. */
.atlas-slide--why-pricing .wpc__card[data-wpc-card="buyer"] {
  top: 376.5px;
  left: 63px;
}

.atlas-slide--why-pricing .wpc__card[data-wpc-card="deal"] {
  top: 376.5px;
  left: 331px;
}

.atlas-slide--why-pricing .wpc__card[data-wpc-card="product"] {
  top: 376.5px;
  left: 599px;
}

.atlas-slide--why-pricing .wpc__card[data-wpc-card="base-rate"] {
  top: 596.5px;
  left: 63px;
}

.atlas-slide--why-pricing .wpc__card[data-wpc-card="premiums"] {
  top: 596.5px;
  left: 331px;
}

.atlas-slide--why-pricing .wpc__card[data-wpc-card="calculation"] {
  top: 596.5px;
  left: 599px;
}

.atlas-slide--why-pricing .wpc__card:hover,
.atlas-slide--why-pricing .wpc__card.is-active {
  border-color: var(--wpc-card-active);
  background: rgba(27, 28, 58, 0.86);
  box-shadow:
    0 10px 28px rgba(0, 0, 0, 0.40),
    0 0 24px rgba(255, 118, 102, 0.22),
    inset 0 1px 0 rgba(255, 255, 255, 0.10);
  transform: translateY(-1px);
}

.atlas-slide--why-pricing .wpc__card:focus-visible {
  outline: 3px solid #FFFFFF;
  outline-offset: 4px;
  border-color: var(--wpc-card-active);
}

.atlas-slide--why-pricing .wpc__card:active {
  transform: translateY(0);
}

.atlas-slide--why-pricing .wpc__icon {
  position: absolute;
  top: 26.5px;
  left: 50%;
  width: 48px;
  height: 48px;
  transform: translateX(-50%);
  color: var(--wpc-card-icon);
  filter: drop-shadow(0 0 4px rgba(255, 118, 102, 0.40));
}

.atlas-slide--why-pricing .wpc__icon img {
  position: absolute;
  top: 50%;
  left: 50%;
  display: block;
  max-width: none;
  object-fit: fill;
  transform: translate(-50%, -50%);
}

.atlas-slide--why-pricing [data-wpc-card="buyer"] .wpc__icon img {
  width: 47.1753px;
  height: 30.0225px;
}

.atlas-slide--why-pricing [data-wpc-card="deal"] .wpc__icon img {
  width: 36px;
  height: 39px;
}

.atlas-slide--why-pricing [data-wpc-card="product"] .wpc__icon img {
  width: 39px;
  height: 33px;
}

.atlas-slide--why-pricing [data-wpc-card="base-rate"] .wpc__icon img {
  width: 39px;
  height: 39px;
}

.atlas-slide--why-pricing [data-wpc-card="premiums"] .wpc__icon img {
  width: 30.0012px;
  height: 39.0012px;
}

.atlas-slide--why-pricing [data-wpc-card="calculation"] .wpc__icon img {
  width: 33px;
  height: 39px;
}

.atlas-slide--why-pricing .wpc__label {
  position: absolute;
  top: 82.5px;
  left: 12px;
  width: 228px;
  height: 24px;
  margin: 0;
  font-family: var(--font-family-inspire);
  font-size: 24px;
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  line-height: 24px;
}

/* Sub copy trimmed from 22px to 19px (with the 12px card gutters
 * above) so the longest approved two-line block ("Marketplace /
 * Deal Season" + "Effective Dates") renders its first line intact
 * instead of wrapping into an unwanted third line. Height covers
 * exactly 2 lines (20px each); .wpc__sub content never exceeds 2
 * lines by copy design. */
.atlas-slide--why-pricing .wpc__sub {
  position: absolute;
  top: 122.5px;
  left: 12px;
  width: 228px;
  height: 44px;
  margin: 0;
  font-family: var(--font-family-inspire);
  font-size: 19px;
  font-weight: var(--font-caption); /* InspireTWDC Roman 400 */
  line-height: 20px;
  overflow: hidden;
}

/* 2026-08-12: connectors restored to run the FULL gap so the flow
 * reads as one continuous line (card group -> RCM -> Core Planning),
 * instead of a short dot+line+arrowhead glyph floating in empty
 * space with nothing touching either card. Card group right edge
 * stays 851 (599 + 252); RCM right edge is 1143 + 192 = 1335. Both
 * gaps are exactly 292px (card->RCM and RCM->Core Planning), so each
 * connector now spans its full 292px gap edge-to-edge: the circular
 * node sits flush against the trailing card/window edge, the
 * arrowhead sits flush against the leading edge of the next card/
 * window, and the line fills everything in between. Both arrows stay
 * on the shared workflow centerline (y=581, see .wpc__window--rcm /
 * --cp), vertically centered on their own 19px height: top = 581 -
 * 19/2 = 571.5. z-index:1 keeps connectors above the slide
 * background but below cards/windows (2) and the dimmed overlay (3),
 * per the requested stacking order. */
.atlas-slide--why-pricing .wpc__arrow {
  z-index: 1;
  width: 292px;
  height: 19px;
}

.atlas-slide--why-pricing .wpc__arrow--one {
  top: 571.5px;
  left: 851px;
}

.atlas-slide--why-pricing .wpc__arrow--two {
  top: 571.5px;
  left: 1335px;
}

.atlas-slide--why-pricing .wpc__arrow img {
  position: absolute;
  display: block;
  max-width: none;
}

.atlas-slide--why-pricing .wpc__arrow .wpc__arrow-dot {
  top: 4px;
  left: 0;
  width: 11px;
  height: 11px;
}

/* Line stretches non-uniformly to fill the new width (the source SVG
 * sets preserveAspectRatio="none"), so it always reaches from the
 * node to the arrowhead regardless of viewport/scale. Height bumped
 * 1.76 -> 2px for the requested "subtle but clearly visible ~2px
 * stroke" without changing the stroke color. */
.atlas-slide--why-pricing .wpc__arrow .wpc__arrow-line {
  top: 10px;
  left: 10px;
  width: 272px;
  height: 2px;
}

.atlas-slide--why-pricing .wpc__arrow .wpc__arrow-head {
  top: 1px;
  left: 282.012px;
  width: 9.98821px;
  height: 16.0974px;
}

.atlas-slide--why-pricing .wpc__window {
  z-index: 2;
  box-sizing: border-box;
  width: 192px;
  height: 160px;
  border-radius: 16px;
  background: var(--wpc-flow-surface);
  filter: drop-shadow(0 10px 14px rgba(0, 0, 0, 0.32));
  overflow: hidden;
}

/* 2026-08-06: both windows share top:501px (center y=581, matching
 * the card group's own vertical center and both arrows) instead of
 * the previous 8px mismatch (497 vs 505). Left offsets follow the
 * consistent 70px gap chain: RCM = arrow1 right (1073) + 70 = 1143;
 * Core Planning = arrow2 right (1557) + 70 = 1627 (right edge 1819,
 * inside the 1857px safe margin used elsewhere in the deck). */
.atlas-slide--why-pricing .wpc__window--rcm {
  top: 501px;
  left: 1143px;
  border: 2px solid var(--wpc-flow-gold);
}

.atlas-slide--why-pricing .wpc__window--cp {
  top: 501px;
  left: 1627px;
  border: 1px solid var(--wpc-flow-border);
}

.atlas-slide--why-pricing .wpc__window-strip {
  display: none;
}

.atlas-slide--why-pricing .wpc__window-body {
  position: absolute;
  inset: 0;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 16px 48px;
  border: 0;
  border-radius: 16px;
  background: var(--wpc-flow-surface);
  box-shadow: inset 0 1px 12px rgba(255, 255, 255, 0.08);
}

.atlas-slide--why-pricing .wpc__window--rcm .wpc__window-body,
.atlas-slide--why-pricing .wpc__window--cp .wpc__window-body {
  border: 0;
  box-shadow: inset 0 1px 12px rgba(255, 255, 255, 0.08);
}

.atlas-slide--why-pricing .wpc__window-icon {
  position: relative;
  display: block;
  flex: 0 0 72px;
  width: 72px;
  height: 72px;
}

.atlas-slide--why-pricing .wpc__window-glyph {
  position: absolute;
  display: block;
  max-width: none;
}

.atlas-slide--why-pricing .wpc__window--rcm .wpc__window-glyph {
  top: 9px;
  left: 9px;
  width: 60.7493px;
  height: 60.7493px;
}

.atlas-slide--why-pricing .wpc__window--cp .wpc__window-glyph {
  top: 4.5px;
  left: 11.25px;
  width: 49.5px;
  height: 60.7496px;
}

.atlas-slide--why-pricing .wpc__window-label {
  width: 160px;
  color: #F1F2F7;
  font-family: var(--font-family-inspire);
  font-size: 24px;
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  line-height: normal;
}

.atlas-slide--why-pricing .wpc__window--rcm .wpc__window-label,
.atlas-slide--why-pricing .wpc__window--cp .wpc__window-label {
  color: #F1F2F7;
}

/* 2026-08-06: recentered again after the card-group enlargement
 * shifted both windows right. RCM center = 1143 + 192/2 = 1239;
 * Core Planning center = 1627 + 192/2 = 1723. Both windows now share
 * the same top/height, so both captions also share the same top:
 * window bottom (501 + 160 = 661) + 36 = 697. */
.atlas-slide--why-pricing .wpc__caption--rcm {
  top: 697px;
  left: 1103.5px;
  width: 271px;
  text-align: center;
  white-space: normal;
}

.atlas-slide--why-pricing .wpc__caption--planning {
  top: 697px;
  left: 1582px;
  width: 282px;
  text-align: center;
  white-space: normal;
}

.atlas-slide--why-pricing .wpc__brand svg {
  visibility: hidden;
}

.atlas-slide--why-pricing .wpc__brand::before {
  position: absolute;
  inset: 0;
  content: "";
  background: url("./assets/atlas/wpc-logo-latest.svg") center / 36px 41px no-repeat;
}

/* 2026-08-12: .wpc-modal no longer carries its own z-index. It used
 * to bundle the scrim and panel into one atomic z:20 stacking bubble,
 * which meant the "selected card" (a sibling, outside this bubble)
 * could never sit visually between the scrim and the panel. With
 * z-index left auto here, .wpc-modal__scrim / .wpc-modal__panel
 * compare directly against .wpc__card / .wpc__arrow / .wpc__window
 * using their own explicit z-index values below, giving the exact
 * requested back-to-front order: connectors (1) -> cards/windows (2)
 * -> dimmed overlay (3) -> selected card (4) -> popover (5). */
.atlas-slide--why-pricing .wpc-modal {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  pointer-events: none;
  font-family: "Open Sans", system-ui, sans-serif;
}

.atlas-slide--why-pricing .wpc-modal[hidden] {
  display: none;
}

.atlas-slide--why-pricing .wpc-modal.is-pinned {
  pointer-events: auto;
}

.atlas-slide--why-pricing .wpc-modal__scrim {
  position: absolute;
  z-index: 3;
  inset: 0;
  background: rgba(2, 0, 36, 0.64);
  opacity: 0;
  pointer-events: none;
  transition: opacity 160ms ease;
}

.atlas-slide--why-pricing .wpc-modal.is-pinned .wpc-modal__scrim {
  opacity: 1;
  pointer-events: auto;
}

/* 2026-08-06: panel widened 480 -> 524px (+9.2%) so the labeled
 * value rows and supporting note have comfortable breathing room at
 * the slightly larger 19-20px title / 17px body type. max-height
 * dropped from an oversized 900px to 420px, a true safety cap; the
 * six modals are short enough that actual height is always content-
 * driven, never the cap. Shadow stays a single restrained ADS
 * elevation (no second, heavier layer) since the modal only needs
 * enough separation to read over the dimmed slide.
 * 2026-08-12: z-index raised 1 -> 5 (see the .wpc-modal comment
 * above) so the popover always paints above the elevated selected
 * card. Position is overridden inline to `absolute` by
 * positionPanel() in app.js, which anchors the panel beside the
 * clicked/hovered card; `relative` here is just the pre-JS fallback
 * so the grid's place-items:center still centers it if JS is slow
 * to run. */
.atlas-slide--why-pricing .wpc-modal__panel {
  position: relative;
  z-index: 5;
  display: flex;
  flex-direction: column;
  width: 524px;
  max-height: 420px;
  overflow: hidden;
  border: 1px solid rgba(15, 18, 20, 0.10);
  border-radius: 12px;
  background: #FFFFFF;
  box-shadow: 0 8px 24px rgba(15, 18, 20, 0.18);
  color: #3B4347;
  outline: none;
  pointer-events: auto;
  animation: wpc-modal-in 160ms ease-out;
}

@keyframes wpc-modal-in {
  from {
    opacity: 0;
    transform: scale(0.98);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

/* Header left inset (20px) matches .wpc-modal__body's left padding
 * below so title, intro, values, and note all align to one shared
 * left edge. */
.atlas-slide--why-pricing .wpc-modal__header {
  display: flex;
  flex: 0 0 52px;
  align-items: center;
  justify-content: space-between;
  box-sizing: border-box;
  height: 52px;
  padding: 0 20px;
  border-bottom: 1px solid rgba(15, 18, 20, 0.10);
}

.atlas-slide--why-pricing .wpc-modal__title {
  min-width: 0;
  margin: 0;
  color: #3B4347;
  font-size: 20px;
  font-weight: 700;
  line-height: 1.4;
}

.atlas-slide--why-pricing .wpc-modal__close {
  position: relative;
  display: grid;
  flex: 0 0 24px;
  place-items: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: 3px;
  background: transparent;
  cursor: pointer;
}

.atlas-slide--why-pricing .wpc-modal__close:hover {
  background: rgba(15, 18, 20, 0.05);
}

.atlas-slide--why-pricing .wpc-modal__close:focus-visible {
  outline: 2px solid #3B4347;
  outline-offset: 2px;
}

.atlas-slide--why-pricing .wpc-modal__close img {
  width: 15.0008px;
  height: 15.0008px;
}

.atlas-slide--why-pricing .wpc-modal__body {
  box-sizing: border-box;
  padding: 16px 20px 20px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* All six modals share this exact 4-part structure: intro question,
 * labeled values, optional one-sentence note. gap:10px is the base
 * rhythm between the question and the values block; .wpc-modal__facts
 * and .wpc-modal__note add their own small top offset on top of this
 * so the note reads as a distinct, but not disconnected, section. */
.atlas-slide--why-pricing .wpc-modal__content {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.atlas-slide--why-pricing .wpc-modal__content[hidden] {
  display: none;
}

.atlas-slide--why-pricing .wpc-modal__content p {
  margin: 0;
  color: #3B4347;
  font-size: 17px;
  font-weight: 400;
  line-height: 1.45;
  white-space: nowrap;
}

.atlas-slide--why-pricing .wpc-modal__content .wpc-modal__question {
  font-size: 19px;
  font-weight: 600;
  line-height: 1.4;
  white-space: normal;
}

.atlas-slide--why-pricing .wpc-modal__content strong {
  font-weight: 600;
}

.atlas-slide--why-pricing .wpc-modal__facts {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.atlas-slide--why-pricing .wpc-modal__content .wpc-modal__note {
  padding-top: 2px;
  color: #51585B;
  font-size: 17px;
  line-height: 1.45;
  white-space: normal;
}

@media (prefers-reduced-motion: reduce) {
  .atlas-slide--why-pricing .wpc__card,
  .atlas-slide--why-pricing .wpc-modal__scrim {
    transition: none;
  }

  .atlas-slide--why-pricing .wpc-modal__panel {
    animation: none;
  }
}

/* ============================================================================
 * SLIDE 7: Future Rate Card Workflow, Figma 492:28287
 * ========================================================================== */

.atlas-slide--future-rate-card {
  --frw-navy: #020024;
  --frw-white: #fff;
  --frw-import-text: #eceeee;
  --frw-card-text: #f5f7fa;
  --frw-support-text: #b8bcc7;
  --frw-border: #4b5a6b;
  --frw-arrow: #6567ce;
  --frw-card-gradient: linear-gradient(33.651905432085904deg, rgb(48,57,70) 0.9976%, rgb(32,40,51) 21.967%, rgb(39,49,61) 104.5%);
  --frw-outer-shadow: 0 12px 16.8px rgba(0,0,0,.32);
  --frw-inner-shadow: inset 0 1.2px 14.4px rgba(255,255,255,.08);
  overflow: hidden;
  border-radius: 40px;
  background: var(--frw-navy);
}

.atlas-slide--future-rate-card .frw {
  position: absolute;
  inset: 0;
  font-family: "InspireTWDC", "MultiplaneTWDC Display", "Open Sans", sans-serif;
  text-rendering: geometricPrecision;
}

.atlas-slide--future-rate-card .frw__header {
  position: absolute;
  left: 63px;
  top: 88px;
  width: 1828px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Restored to the "InspireTWDC Heavy" face recorded for this slide. A
 * 2026-08-05 change had switched title and subtitle to Black to give the
 * deck one shared title treatment; that was a deviation from the source. */
.atlas-slide--future-rate-card .frw__title {
  margin: 0;
  width: 1727px;
  font-family: var(--font-family-inspire);
  font-size: 56px;
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
  line-height: 1;
  color: var(--frw-white);
  white-space: nowrap;
}

.atlas-slide--future-rate-card .frw__subtitle {
  margin: 0;
  width: 1808px;
  font-family: var(--font-family-inspire);
  font-size: 36px;
  font-weight: var(--font-subheading); /* InspireTWDC Heavy 600 */
  line-height: 56px;
  color: var(--frw-white);
  /* 2026-08-05: normal (not nowrap) for the same reason as the other
   * three slides' subtitles: this text already fits on one line at
   * 1808px, but natural wrapping keeps every subtitle on the deck
   * behaving the same way regardless of font-fallback width. */
  white-space: normal;
}

/* 2026-08-05: widened from 320px cards / 1474.6px total (centered
 * with ~160px of slack on each side) to fill the full shared content
 * region (63px -> 1857px, same margins as the title) so every card
 * title fits on one line, with margin to spare, instead of
 * overflowing its card. Connector columns keep the same 181.33px
 * arrow asset centered in a 199.8px slot; card columns absorb the
 * rest: 3 x 464.8 + 2 x 199.8 = 1794px content width. */
.atlas-slide--future-rate-card .frw__workflow {
  position: absolute;
  left: 63px;
  top: 388px;
  display: grid;
  grid-template-columns: 464.8px 199.8px 464.8px 199.8px 464.8px;
  align-items: start;
  width: 1794px;
}

.atlas-slide--future-rate-card .frw__item {
  display: grid;
  grid-template-columns: 4.8px 460px;
  grid-template-rows: 216px auto;
  row-gap: 43px;
  width: 464.8px;
}

.atlas-slide--future-rate-card .frw__card {
  position: relative;
  grid-column: 2;
  grid-row: 1;
  width: 460px;
  height: 216px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1.2px solid var(--frw-border);
  border-radius: 19.2px;
  background: var(--frw-card-gradient);
  box-shadow: var(--frw-outer-shadow), var(--frw-inner-shadow);
}

.atlas-slide--future-rate-card .frw__card-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  gap: 9.6px;
  padding: 19.2px 16px;
}

.atlas-slide--future-rate-card .frw__icon {
  width: 86.4px;
  height: 86.4px;
  display: grid;
  place-items: center;
  flex: none;
}

.atlas-slide--future-rate-card .frw__icon img {
  display: block;
  max-width: none;
  -webkit-user-drag: none;
  user-select: none;
}

.atlas-slide--future-rate-card .frw__card-title {
  display: flex;
  flex: 0 0 40px;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  margin: 0;
  width: 100%;
  height: 40px;
  font-family: var(--font-family-inspire);
  /* 2026-08-05: 33.6px -> 32px, a small (~5%) reduction paired with the
   * wider card above so "Use in Pricing Workflows" (the longest of the
   * three titles) fits on one line with margin to spare instead of
   * overflowing the card. Applies uniformly to all three card titles. */
  font-size: 32px;
  font-weight: var(--font-label); /* InspireTWDC Black 800 */
  line-height: 40px;
  color: var(--frw-card-text);
  text-align: center;
  white-space: nowrap;
}

.atlas-slide--future-rate-card .frw__item--import .frw__card-title {
  color: var(--frw-import-text);
}

/* 2026-08-05: widened to match the new 446.5px card (was 313.2px,
 * inset under the old 320px card) and switched from a fixed 73.5px
 * height to natural auto height (the grid row above is now "auto"
 * too), so the sentence wraps on its own within the wider box
 * instead of relying on manually inserted <span> line breaks that
 * produced 4 uneven lines per card. */
.atlas-slide--future-rate-card .frw__support {
  grid-column: 2;
  grid-row: 2;
  align-self: start;
  justify-self: center;
  box-sizing: border-box;
  width: 453px;
  margin: 0;
  font-family: var(--font-family-inspire);
  font-size: 30.617px;
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
  line-height: 1.2;
  color: var(--frw-support-text);
  text-align: center;
}

.atlas-slide--future-rate-card .frw__connector {
  position: relative;
  left: 2.4px;
  align-self: start;
  justify-self: center;
  width: 181.33px;
  height: 22.248px;
  margin-top: 96.876px;
}

.atlas-slide--future-rate-card .frw__connector img {
  position: absolute;
  display: block;
  max-width: none;
  -webkit-user-drag: none;
  user-select: none;
}

.atlas-slide--future-rate-card .frw__arrow-dot {
  left: 0;
  top: 4.752px;
  width: 13.2px;
  height: 13.2px;
}

.atlas-slide--future-rate-card .frw__arrow-line {
  left: 12.096px;
  top: 9.873px;
  width: 157.2px;
  height: 2.11499px;
}

.atlas-slide--future-rate-card .frw__arrow-head {
  left: 169.344px;
  top: 1.508px;
  width: 11.9859px;
  height: 19.3169px;
}

.atlas-slide--future-rate-card .frw__brand {
  position: absolute;
  left: 1826px;
  top: 997px;
  width: 36px;
  height: 41px;
  display: block;
  max-width: none;
  -webkit-user-drag: none;
  user-select: none;
}

/* ============================================================================
 * (legacy .atlas-slide--pricing-flow / .pflow* rules removed 2026-07-07
 * along with the slide 4 rebuild; the earlier 3x3 grid + browser cards
 * composition is no longer in the DOM.)
 * ========================================================================== */

/* ============================================================================
 * WIREFRAME THEME (2026-06-29 brief)
 *
 * Selected via the profile menu (Marge avatar -> Theme -> Wireframe). The
 * intent is a black-and-white UX-review skin that preserves every layout,
 * spacing, sizing, and interaction detail of the Ad Design System theme
 * but strips out all final-visual color. Brand indigo, danger red, status
 * pill fills, and macOS/Excel/Finder accent colors all collapse to a
 * neutral grayscale palette.
 *
 * Implementation strategy:
 *   1. Redefine every ADS token + legacy --indigo-* / --nav-* / --danger-*
 *      token to a grayscale equivalent. This single change cascades to
 *      90%+ of the app because every styled component already pulls from
 *      tokens (buttons, links, chips, modal, dropdowns, datepicker,
 *      pagination, toast, accordion).
 *   2. Add high-specificity overrides for the small handful of places
 *      that hardcode a brand hex inline (nav background, table active
 *      page button, danger button, action icons, ADS toast accent,
 *      Finder sidebar icons, Excel chrome green).
 *   3. NO layout / sizing / typography changes. This is theme-level
 *      visual transformation only.
 *
 * Grayscale palette (all values picked to keep AA contrast):
 *   #FFFFFF  pure white                surface, default backgrounds
 *   #F7F7F7  very-light gray          page band, table-head, hover wash
 *   #ECECEC  light gray               chip / disabled fill
 *   #D8D8D8  light-gray border        cards, inputs, modal, table dividers
 *   #B3B3B3  mid gray                 muted icons, placeholder
 *   #8E8E8E  mid-dark gray            secondary text
 *   #4D4D4D  dark gray                primary text (matches ADS text/primary
 *                                      contrast against white)
 *   #1A1A1A  near-black               headings, emphasis, hover text
 *   #000000  black                    primary button + accent
 * ========================================================================== */

body.theme-wireframe {
  /* ---- Core ADS token overrides (cascades to nearly everything) ---- */
  --ads-brand:           #1A1A1A;          /* primary fill = near-black */
  --ads-brand-hover:     #000000;
  --ads-brand-wash:      #ECECEC;          /* selected-row wash */
  --ads-text-primary:    #1A1A1A;
  --ads-text-secondary:  #4D4D4D;
  --ads-text-tertiary:   #8E8E8E;
  --ads-text-placeholder:#B3B3B3;
  --ads-text-disabled:   #B3B3B3;
  --ads-text-on-primary: #FFFFFF;
  --ads-border-default:  #D8D8D8;
  --ads-border-bold:     #8E8E8E;
  --ads-border-recessed: #ECECEC;
  --ads-border-subtle:   #F0F0F0;
  --ads-surface:         #FFFFFF;
  --ads-surface-hover:   rgba(0, 0, 0, 0.04);
  --ads-surface-active:  rgba(0, 0, 0, 0.08);
  --ads-surface-table-head: #F7F7F7;
  --ads-scrim:           rgba(0, 0, 0, 0.50);
  --ads-shadow-popover:  0 4px 12px rgba(0, 0, 0, 0.20);
  --ads-shadow-sheet:    0 4px 16px rgba(0, 0, 0, 0.20);
  --ads-shadow-card:     0 1px 4px rgba(0, 0, 0, 0.10);

  /* ---- Field tokens ---- */
  --ads-input-border-rest:   #D8D8D8;
  --ads-input-border-hover:  #8E8E8E;
  --ads-input-border-active-color: #1A1A1A;
  --ads-input-border-error:  #8E8E8E;       /* error -> mid gray (no red) */
  --ads-input-border-disabled: #ECECEC;
  --ads-input-bg-disabled:   #FFFFFF;
  --ads-input-text:          #1A1A1A;
  --ads-input-text-disabled: #B3B3B3;
  --ads-input-placeholder:   #B3B3B3;
  --ads-input-error-text:    #4D4D4D;       /* error text -> dark gray */
  --ads-label-color:         #4D4D4D;

  /* ---- Danger / status tokens (red -> grayscale) ---- */
  --ads-danger-bg:           #1A1A1A;       /* destructive button = black */
  --ads-danger-hover:        #000000;
  --ads-danger-border:       #8E8E8E;
  --ads-danger-text:         #4D4D4D;
  --ads-danger-icon:         #1A1A1A;
  --ads-positive-text:       #4D4D4D;       /* success -> dark gray */
  --ads-positive-bg:         #ECECEC;

  /* ---- Popover / dropdown ---- */
  --ads-popover-bg:          #FFFFFF;
  --ads-popover-border:      #D8D8D8;
  --ads-option-selected-bg:  #ECECEC;

  /* ---- Chip / toast ---- */
  --ads-chip-info-bg:        #ECECEC;

  /* ---- Legacy EDL tokens (kept so anything still referencing them
   *      reads grayscale too) ---- */
  --indigo-70: #1A1A1A;
  --indigo-30: #4D4D4D;
  --indigo-15: #D8D8D8;
  --indigo-10: #ECECEC;
  --indigo-5:  #F7F7F7;
  --gray-95: #1A1A1A;
  --gray-70: #4D4D4D;
  --gray-50: #8E8E8E;
  --gray-40: #B3B3B3;
  --gray-30: #D8D8D8;
  --gray-20: #ECECEC;
  --gray-10: #F0F0F0;
  --gray-05: #F7F7F7;
  --page-bg: #F7F7F7;
  --surface: #FFFFFF;
  --nav-bg:  #1A1A1A;
  --nav-text:#FFFFFF;
  --danger-bg: #ECECEC;
  --danger-text: #4D4D4D;
  --danger-btn: #1A1A1A;
  --danger-btn-hover: #000000;
  --scrim: rgba(0, 0, 0, 0.50);
}

/* -------- High-specificity overrides for hardcoded brand hex values
 *          that don't pull from tokens. Each rule lists the original
 *          ADS / EDL color it's replacing for traceability. ----------- */

/* Global navigation bar */
body.theme-wireframe .gnav {
  background: #1A1A1A;              /* was indigo #4045C2 */
  color: #FFFFFF;
}
body.theme-wireframe .gnav__item--active {
  color: #FFFFFF;
}
/* Nav active underline (2px white -> 2px lighter gray for wireframe) */
body.theme-wireframe .gnav__item--active::after,
body.theme-wireframe .gnav__items .gnav__item--active::after {
  background: #FFFFFF;
}

/* Filter button + secondary buttons - replace ad-hoc indigo borders */
body.theme-wireframe .filter-btn,
body.theme-wireframe [data-action="toggle-filter"] {
  color: #1A1A1A !important;
  border-color: #D8D8D8 !important;
}
body.theme-wireframe .filter-btn:hover,
body.theme-wireframe [data-action="toggle-filter"]:hover {
  border-color: #8E8E8E !important;
  background: rgba(0, 0, 0, 0.04);
}

/* Primary button - drop the indigo fill, use black outline button look */
body.theme-wireframe .btn--primary,
body.theme-wireframe .page__actions .btn--primary,
body.theme-wireframe .fpanel__footer .btn--primary {
  background: #1A1A1A;
  color: #FFFFFF;
  border-color: #1A1A1A;
}
body.theme-wireframe .btn--primary:hover,
body.theme-wireframe .page__actions .btn--primary:hover,
body.theme-wireframe .fpanel__footer .btn--primary:hover {
  background: #000000;
  border-color: #000000;
}
/* Secondary button - flat white with mid-gray border */
body.theme-wireframe .btn--secondary {
  background: #FFFFFF;
  color: #1A1A1A;
  border-color: #8E8E8E;
}
body.theme-wireframe .btn--secondary:hover {
  background: #F7F7F7;
  border-color: #4D4D4D;
}
/* Tertiary / ghost buttons */
body.theme-wireframe .btn--tertiary,
body.theme-wireframe .btn--ghost {
  color: #4D4D4D;
}
body.theme-wireframe .btn--tertiary:hover,
body.theme-wireframe .btn--ghost:hover {
  background: rgba(0, 0, 0, 0.04);
  color: #1A1A1A;
}
/* Destructive button - per brief: "Destructive buttons: black/white
 * wireframe treatment only; do not use red." */
body.theme-wireframe .btn--danger {
  background: #FFFFFF;
  color: #1A1A1A;
  border-color: #1A1A1A;
}
body.theme-wireframe .btn--danger:hover {
  background: #1A1A1A;
  color: #FFFFFF;
  border-color: #1A1A1A;
}

/* Status chips - outline-only gray pills, no fill */
body.theme-wireframe .chip,
body.theme-wireframe .chip--published,
body.theme-wireframe .chip--draft,
body.theme-wireframe .rcm-status-chip,
body.theme-wireframe .rcm-status-chip--published,
body.theme-wireframe .rcm-status-chip--draft {
  background: #FFFFFF !important;
  color: #1A1A1A !important;
  border: 1px solid #8E8E8E !important;
}

/* Table - link color flat black, header bg light gray */
body.theme-wireframe .name__link,
body.theme-wireframe .row a {
  color: #1A1A1A;
}
body.theme-wireframe .name__link:hover {
  color: #000000;
  text-decoration: underline;
}
body.theme-wireframe .table__head {
  background: #F7F7F7;
}
body.theme-wireframe .row {
  border-bottom-color: #ECECEC;
}

/* Action icons - drop indigo, use mid-dark gray */
body.theme-wireframe .icon-btn,
body.theme-wireframe .row .actions .icon-btn,
body.theme-wireframe .theme-ads .icon-btn,
body.theme-wireframe .theme-ads .row .actions .icon-btn {
  color: #4D4D4D !important;
}
body.theme-wireframe .icon-btn:hover,
body.theme-wireframe .row .actions .icon-btn:hover {
  color: #1A1A1A !important;
}
/* Delete-icon hover stays neutral too (no semantic red) */
body.theme-wireframe .icon-btn[aria-label^="Delete"]:hover,
body.theme-wireframe .row .actions .icon-btn[aria-label^="Delete"]:hover {
  color: #1A1A1A !important;
}

/* Pagination - active pill = black, not indigo */
body.theme-wireframe .page-btn--num.is-active {
  background: #1A1A1A;
  color: #FFFFFF;
  border-color: #1A1A1A;
}
body.theme-wireframe .page-btn:focus-visible,
body.theme-wireframe .page-btn--chevron:focus-visible {
  outline-color: #1A1A1A;
}

/* Search field outline neutralized */
body.theme-wireframe .search__input {
  border-color: #D8D8D8;
  color: #1A1A1A;
}
body.theme-wireframe .search__input:focus {
  border-color: #1A1A1A;
  box-shadow: none;
}

/* Modal headers carry no leading icon and confirmations use the ADS Primary
 * button, so the wireframe theme has nothing modal-specific left to neutralize
 * here: both surfaces already follow the shared button rules above. */

/* Accordion error accent (3px left edge) - red -> dark gray */
body.theme-wireframe .accordion.accordion--has-errors {
  box-shadow: inset 3px 0 0 #4D4D4D;
}
body.theme-wireframe .accordion__status--error {
  color: #1A1A1A;
}
body.theme-wireframe .accordion__status--ok,
body.theme-wireframe .accordion__status--ok svg {
  color: #4D4D4D;
}

/* Field error inline message - red border + red text -> gray + gray */
body.theme-wireframe .field.is-invalid .field__input,
body.theme-wireframe .field.is-invalid .edl-select__trigger,
body.theme-wireframe .field.is-invalid .ads-datepicker__trigger {
  border-color: #4D4D4D !important;
}
body.theme-wireframe .field__error,
body.theme-wireframe .theme-ads .field__error {
  color: #4D4D4D;
}
body.theme-wireframe .field__error-icon,
body.theme-wireframe .theme-ads .field__error-icon {
  color: #4D4D4D;
}

/* Vertical nav (Version 1.1) - indigo selected -> gray selected */
body.theme-wireframe .vnav__item--active {
  background: rgba(0, 0, 0, 0.06);
  color: #1A1A1A;
}
body.theme-wireframe .vnav__item--active::before {
  background: #1A1A1A;
}
body.theme-wireframe .vnav__icon {
  color: #4D4D4D;
}
body.theme-wireframe .vnav__item--active .vnav__icon {
  color: #1A1A1A;
}

/* Brand-name wordmark next to the Atlas brand icon */
body.theme-wireframe .gnav__brand-name {
  color: #FFFFFF;
}
body.theme-wireframe .gnav__brand-icon {
  /* The Disney "D" is a currentColor SVG so it already tracks the
   * header's white text. Explicit override so a future indigo
   * fallback would still resolve to white in wireframe mode. */
  color: #FFFFFF;
}

/* Loading / hover blue washes -> neutral gray washes */
body.theme-wireframe .ads-cal__day--today,
body.theme-wireframe .ads-datepicker__trigger:hover,
body.theme-wireframe .edl-select__trigger:hover {
  border-color: #8E8E8E !important;
}

/* Banish any remaining brand-tinted backgrounds */
body.theme-wireframe .qsheet__head,
body.theme-wireframe .qsheet__footer {
  background: #FFFFFF;
}
body.theme-wireframe [class*="--brand"]:not(.btn) {
  /* Catch-all for any class with "brand" in the name we haven't explicitly
   * targeted - neutralize background + text to grayscale. */
  background: #FFFFFF;
  color: #1A1A1A;
}

/* ============================================================================
 * RATE CARD LAYER EXPLAINER (.rcle)
 * Live, scoped presentation overlay for the Rate Card Manager UI.
 * ========================================================================== */
.rcle {
  --rcle-navy: #020024;
  --rcle-surface: #FFFFFF;
  --rcle-muted-white: rgba(255, 255, 255, 0.72);
  --rcle-line-accent: #2097F6;
  --rcle-focus: #6567CE;
  --rcle-border: rgba(255, 255, 255, 0.18);
  --rcle-surface-border: #E5E7EB;
  --rcle-shadow: var(--ads-shadow-sheet, 0 4px 16px rgba(15, 18, 20, 0.30));

  position: fixed;
  inset: 0;
  z-index: 10000;
  box-sizing: border-box;
  width: 100%;
  height: 100dvh;
  /* The same breathing room the deck leaves around its stage, so the
   * charcoal reads as a canvas the presentation sits on rather than as the
   * page having gone dark. */
  padding:
    max(var(--presentation-safe-inset), env(safe-area-inset-top, 0px))
    max(var(--presentation-safe-inset), env(safe-area-inset-right, 0px))
    max(var(--presentation-safe-inset), env(safe-area-inset-bottom, 0px))
    max(var(--presentation-safe-inset), env(safe-area-inset-left, 0px));
  overflow: hidden;
  /* Centre the canvas in the safe box, the way the deck centres its
   * stage, so the charcoal shows on all four sides. The single track is
   * given an explicit size: left to size itself around the canvas, the
   * canvas's own max-height had nothing definite to resolve against and
   * the whole shell shrank whenever the view inside it got taller. */
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  place-items: center;
  /* Outer presentation canvas. --rcle-navy stays the overlay's own accent
   * navy (callout markers, scrims), which is not a background. */
  background: var(--presentation-canvas);
  color: var(--rcle-surface);
  font-family: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
}

.rcle[hidden] {
  display: none;
}

.rcle *,
.rcle *::before,
.rcle *::after {
  box-sizing: border-box;
}

/* Presentation accessibility floor. JavaScript applies this marker only to
 * visible text nodes whose computed size would otherwise fall below 14px.
 * The marker is removed when presentation mode closes, so the production UI
 * retains its original typography outside the explainer. */
.rcle [data-rcle-min-text] {
  font-size: 14px !important;
  line-height: max(18px, 1.35em) !important;
}

/* This panel is the explainer's slide: the counterpart of .atlas-stage.
 * It carries the deck's navy, corner and shadow so the two surfaces are
 * the same object, and clips its own contents so a callout, a scrim or the
 * live product view cannot square off a corner. */
.rcle__panel {
  position: relative;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  gap: clamp(18px, 2.5vh, 32px);
  /* The deck's stage is 1920x1080 scaled to fit its safe box. This is the
   * same canvas expressed for a fluid surface: take the width the height
   * allows, or the full width if that is smaller, and let the ratio set
   * the height. Both surfaces then land on the same shape at the same
   * size, which is what makes the mark and the corner line up. */
  width: min(
    100%,
    calc((100dvh - 2 * var(--presentation-safe-inset)) * (16 / 9))
  );
  aspect-ratio: 1920 / 1080;
  height: auto;
  max-height: 100%;
  min-width: 0;
  min-height: 0;
  padding:
    clamp(28px, 4.5vh, 56px)
    clamp(56px, 5vw, 88px)
    clamp(48px, 5vh, 64px);
  background: var(--presentation-slide-surface);
  border-radius: var(--presentation-slide-radius);
  box-shadow: var(--presentation-slide-shadow);
  overflow: hidden;
  outline: none;
}

.rcle__global-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: clamp(24px, 3vw, 48px);
  min-width: 0;
}

.rcle__global-header > div {
  min-width: 0;
}

.rcle__title {
  margin: 0;
  color: var(--rcle-surface);
  font-family: "InspireTWDC", "MultiplaneTWDC Display", "Open Sans", sans-serif;
  font-size: clamp(34px, 3vw, 54px);
  font-weight: 900;
  line-height: 1;
  letter-spacing: -0.01em;
}

.rcle__subtitle {
  margin: clamp(8px, 1vh, 12px) 0 0;
  color: var(--rcle-muted-white);
  font-size: clamp(18px, 1.45vw, 24px);
  font-weight: 400;
  line-height: 1.4;
}

.rcle__close {
  display: inline-flex;
  flex: 0 0 40px;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin: 0;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--ads-radius-sm, 6px);
  background: transparent;
  color: currentColor;
  cursor: pointer;
  transition: background-color 140ms ease, border-color 140ms ease, transform 100ms ease;
}

.rcle__close:hover {
  border-color: var(--rcle-border);
  background: rgba(255, 255, 255, 0.08);
}

.rcle__close:active {
  background: rgba(255, 255, 255, 0.13);
  transform: scale(0.96);
}

.rcle__close:focus-visible {
  outline: 2px solid var(--rcle-focus);
  outline-offset: 3px;
}

.rcle__layout {
  display: grid;
  grid-template-columns: minmax(240px, 28fr) minmax(0, 72fr);
  gap: clamp(28px, 4vw, 64px);
  min-width: 0;
  min-height: 0;
}

.rcle__explanation {
  display: flex;
  flex-direction: column;
  gap: clamp(18px, 2.2vh, 28px);
  min-width: 0;
  min-height: 0;
  padding-right: 4px;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
}

.rcle__layer-selector {
  display: flex;
  flex: 0 0 auto;
  flex-direction: column;
  gap: 2px;
}

.rcle__layer-option {
  width: 100%;
  min-height: 38px;
  margin: 0;
  padding: 8px 12px;
  border: 0;
  border-left: 3px solid transparent;
  border-radius: 0 var(--ads-radius-sm, 6px) var(--ads-radius-sm, 6px) 0;
  background: transparent;
  color: var(--rcle-muted-white);
  font: inherit;
  font-size: clamp(14px, 1vw, 16px);
  font-weight: 600;
  line-height: 1.35;
  text-align: left;
  cursor: pointer;
  transition: background-color 140ms ease, color 140ms ease, border-color 140ms ease;
}

.rcle__layer-option:hover {
  background: rgba(255, 255, 255, 0.06);
  color: var(--rcle-surface);
}

.rcle__layer-option:active {
  background: rgba(255, 255, 255, 0.11);
}

.rcle__layer-option.is-active,
.rcle__layer-option[aria-current="true"] {
  border-left-color: var(--rcle-line-accent);
  background: rgba(255, 255, 255, 0.10);
  color: var(--rcle-surface);
}

.rcle__layer-option:focus-visible {
  outline: 2px solid var(--rcle-focus);
  outline-offset: 2px;
}

.rcle__copy {
  display: flex;
  flex-direction: column;
  gap: clamp(9px, 1.2vh, 14px);
  min-width: 0;
  transition: opacity 180ms ease;
}

.rcle__copy.is-changing {
  opacity: 0.25;
}

.rcle__layer-label {
  color: var(--rcle-line-accent);
  font-size: clamp(12px, 0.85vw, 14px);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.rcle__section-title {
  margin: 0;
  color: var(--rcle-surface);
  font-family: "InspireTWDC", "MultiplaneTWDC Display", "Open Sans", sans-serif;
  font-size: clamp(30px, 2.45vw, 44px);
  font-weight: 900;
  line-height: 1.08;
  letter-spacing: -0.01em;
}

.rcle__definition {
  margin: 0;
  color: var(--rcle-muted-white);
  font-size: clamp(17px, 1.25vw, 21px);
  line-height: 1.45;
}

.rcle__seeing-title {
  margin: clamp(5px, 0.8vh, 10px) 0 0;
  color: var(--rcle-surface);
  font-size: clamp(12px, 0.85vw, 14px);
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.rcle__seeing-list {
  display: flex;
  flex-direction: column;
  gap: clamp(7px, 1vh, 12px);
  margin: 0;
  padding-left: 1.25em;
  color: var(--rcle-surface);
  font-size: clamp(15px, 1.05vw, 18px);
  line-height: 1.45;
}

.rcle__seeing-list li::marker {
  color: var(--rcle-line-accent);
}

.rcle__callout-legend {
  display: flex;
  flex: 0 0 auto;
  flex-direction: column;
  gap: clamp(6px, 0.8vh, 10px);
  margin: 0;
  padding: 0;
  list-style: none;
}

.rcle__callout-legend[hidden] {
  display: none;
}

.rcle__callout-legend li {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  color: var(--rcle-muted-white);
  font-size: clamp(13px, 0.9vw, 15px);
  line-height: 1.35;
}

.rcle__callout-legend .marker {
  display: inline-flex;
  flex: 0 0 24px;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border: 1px solid var(--rcle-navy);
  border-radius: 50%;
  background: var(--rcle-line-accent);
  color: var(--rcle-surface);
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
}

.rcle__product-frame {
  position: relative;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  border: 1px solid var(--rcle-surface-border);
  border-radius: var(--ads-radius-card, 16px);
  background: var(--rcle-surface);
  box-shadow: var(--rcle-shadow);
  color: initial;
}

.rcle__product-viewport,
.rcle__live-slot {
  width: 100%;
  height: 100%;
  min-width: 0;
  min-height: 0;
  overflow: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
  background: var(--rcle-surface);
}

.rcle__callouts {
  position: absolute;
  inset: 0;
  z-index: 3;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}

.rcle__callouts .target,
.rcle__callouts .target-secondary {
  fill: none;
  stroke: var(--rcle-line-accent);
  stroke-opacity: 0.64;
  stroke-width: 1px;
  rx: 4px;
  ry: 4px;
  vector-effect: non-scaling-stroke;
}

.rcle__callouts .connector,
.rcle__callouts .connector-secondary {
  fill: none;
  stroke: var(--rcle-line-accent);
  stroke-opacity: 0.72;
  stroke-width: 1px;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

.rcle__callouts .target-secondary,
.rcle__callouts .connector-secondary {
  stroke-opacity: 0.42;
}

.rcle__callouts .marker {
  fill: var(--rcle-line-accent);
  stroke: var(--rcle-navy);
  stroke-width: 1px;
  vector-effect: non-scaling-stroke;
}

.rcle__callouts .number {
  fill: var(--rcle-surface);
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  font-weight: 700;
}

.rcle__callouts[hidden],
.rcle__callouts [hidden] {
  display: none;
}

/* Anchored to the canvas at the deck's inset, not to the window. It used
 * to sit 8px off the viewport corner, which put it outside where the
 * canvas now ends. */
.rcle__brand {
  position: absolute;
  right: var(--presentation-brand-right);
  bottom: var(--presentation-brand-bottom);
  z-index: 1;
  display: block;
  width: var(--presentation-brand-width);
  height: auto;
  pointer-events: none;
  user-select: none;
}

body.rcle-open {
  overflow: hidden;
  background: var(--presentation-canvas);
}

html:has(body.rcle-open) {
  background: var(--presentation-canvas);
  height: 100%;
  overflow: hidden;
}

@media (max-width: 1180px) {
  .rcle__panel {
    gap: clamp(16px, 2vh, 24px);
    padding: clamp(24px, 3.5vh, 40px) 52px 52px;
  }

  .rcle__layout {
    grid-template-columns: minmax(224px, 28fr) minmax(0, 72fr);
    gap: clamp(22px, 3vw, 36px);
  }
}

@media (max-width: 1050px) {
  .rcle__panel {
    padding-right: 48px;
    padding-left: 48px;
  }

  .rcle__layout {
    grid-template-columns: minmax(220px, 28fr) minmax(0, 72fr);
    gap: 22px;
  }

  .rcle__layer-option {
    padding-right: 9px;
    padding-left: 9px;
  }
}

@media (max-width: 900px) {
  .rcle__panel {
    gap: 16px;
    padding: 24px 48px 52px;
  }

  .rcle__layout {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, auto) minmax(300px, 1fr);
    gap: 18px;
  }

  .rcle__explanation {
    display: grid;
    grid-template-columns: minmax(180px, 0.7fr) minmax(0, 1.3fr);
    grid-template-rows: auto auto;
    gap: 14px 24px;
    max-height: 42dvh;
  }

  .rcle__layer-selector {
    grid-row: 1 / span 2;
  }

  .rcle__copy {
    gap: 8px;
  }

  .rcle__callout-legend {
    grid-column: 2;
  }
}

@media (max-height: 760px) {
  .rcle__panel {
    gap: 14px;
    padding-top: 20px;
    padding-bottom: 44px;
  }

  .rcle__title {
    font-size: clamp(32px, 2.7vw, 44px);
  }

  .rcle__subtitle {
    margin-top: 5px;
    font-size: clamp(16px, 1.25vw, 20px);
  }

  .rcle__layout {
    gap: clamp(18px, 2.5vw, 30px);
  }

  .rcle__explanation {
    gap: 12px;
  }

  .rcle__layer-option {
    min-height: 32px;
    padding-top: 5px;
    padding-bottom: 5px;
    font-size: 14px;
  }

  .rcle__copy {
    gap: 7px;
  }

  .rcle__section-title {
    font-size: clamp(28px, 2.2vw, 38px);
  }

  .rcle__definition {
    font-size: clamp(15px, 1.05vw, 18px);
    line-height: 1.35;
  }

  .rcle__seeing-list {
    gap: 5px;
    font-size: clamp(14px, 0.95vw, 16px);
    line-height: 1.35;
  }

  .rcle__callout-legend {
    gap: 4px;
  }

  .rcle__callout-legend li {
    font-size: 14px;
    line-height: 1.25;
  }

  /* The mark keeps the shell's size and inset on short viewports too:
   * resizing it here made it change between the deck and the explainer
   * depending on the window. */
}

@media (prefers-reduced-motion: reduce) {
  .rcle__copy,
  .rcle__close,
  .rcle__layer-option {
    transition: none;
  }
}

/* Exploded-view pricing layers presentation. */
.rcle[data-rcle-exploded] {
  --rcle-card-text: var(--ads-color-text-primary, #171C20);
  --rcle-card-muted: var(--ads-color-text-secondary, #59636E);
  --rcle-card-surface: var(--ads-color-surface-default, #FFFFFF);
  --rcle-card-divider: var(--ads-color-border-subtle, #D6DADE);
  --rcle-card-accent: var(--ads-color-brand-primary, #6567CE);
}

.rcle[data-rcle-exploded] .rcle__panel {
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: clamp(14px, 2vh, 24px);
  padding:
    clamp(24px, 3.6vh, 44px)
    clamp(40px, 4vw, 72px)
    clamp(28px, 3.8vh, 46px);
}

.rcle[data-rcle-exploded] .rcle__global-header {
  align-items: center;
}

.rcle[data-rcle-exploded] .rcle__title {
  /* Use the approved InspireTWDC Black face so the title renders in the
   * design system's Black weight rather than the default Heavy that
   * "MultiplaneTWDC Display" resolves to at font-weight 900.
   *
   * Clamped to 30-32 px per the 2026-08-04 brief so the live product
   * stays the dominant visual on both the current-carousel and the
   * previous-design views. */
  font-family: "InspireTWDC Black", "InspireTWDC", "MultiplaneTWDC Display", "Open Sans", sans-serif;
  font-size: clamp(30px, 2vw, 32px);
  line-height: 1.1;
}

.rcle[data-rcle-exploded] .rcle__subtitle {
  /* 16-18 px range so the previous-design supporting explanation
   * (2-3 lines of muted light-gray copy) reads cleanly next to the
   * carousel's shorter per-slide subtitles. */
  margin-top: clamp(6px, 0.9vh, 10px);
  font-size: clamp(16px, 1.05vw, 18px);
  line-height: 1.4;
  max-width: 78ch;
}

.rcle[data-rcle-exploded] .rcle__diagram {
  /* Two columns: the live product on the left, one editorial
   * explanation on the right. The product is the hero, so it takes
   * roughly 70% of the content width against 26% for the copy, with a
   * restrained gutter between them. Both tracks use minmax(0, ...) so a
   * wide table cannot push the grid past the viewport. */
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 70fr) minmax(260px, 26fr);
  /* One bounded row, and columns left to stretch into it. Centring the
     items instead let them size to the live product's natural height,
     which spilled the frame over the navigation below. */
  grid-template-rows: minmax(0, 1fr);
  gap: clamp(32px, 2.6vw, 40px);
  overflow: hidden;
  min-width: 0;
  min-height: 0;
}

.rcle[data-rcle-exploded] .rcle__product-source {
  position: relative;
  /* A bounded row of its own. Without this the column takes the live
     product's natural height, height:100% on the frame resolves against
     an auto-height parent and falls back to content height, and the
     frame overruns the composition to sit on top of the navigation. */
  display: grid;
  grid-template-rows: minmax(0, 1fr);
  overflow: hidden;
  min-width: 0;
  min-height: 0;
  /* The padding used to make room for a "Live product view" label above
     the frame. That label is gone, so the frame gets the height back. */
  padding-top: 0;
}

/* The product does not need to announce itself, and this label was
   colliding with the presentation title in the same corner. */
.rcle[data-rcle-exploded] .rcle__context-label {
  display: none;
}

.rcle[data-rcle-exploded] .rcle__context-label-unused {
  position: absolute;
  top: 0;
  left: 0;
  color: var(--rcle-muted-white);
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.rcle[data-rcle-exploded] .rcle__product-frame {
  width: 100%;
  height: 100%;
  max-height: 100%;
  border-color: var(--rcle-border);
  border-radius: var(--ads-radius-card, 16px);
  background: var(--rcle-card-surface);
  box-shadow: var(--ads-shadow-card-high, var(--rcle-shadow));
}

.rcle[data-rcle-exploded] .rcle__product-viewport {
  overflow: auto;
}

.rcle[data-rcle-exploded] .rcle__live-slot {
  overflow: hidden;
  background: var(--rcle-card-surface);
}

/* The product is no longer dimmed as a whole. A scrim drawn in the
   callout layer darkens everything except the region being explained,
   which is the only way the focused area can stay at full opacity:
   opacity on this root would cap every child with it. */
.rcle[data-rcle-exploded] .rcle__live-slot > [data-v2-root] {
  opacity: 1;
}

/* Carousel column: one .rcle__slide is visible at a time. The column
 * is vertically centered so the active slide sits at the optical
 * center of the live product view. All slides share the same slot so
 * the two neighbors that are hidden do not push the visible slide
 * around when they are shown. */
/* The composition as a whole is centred by the diagram grid; within it
   the copy starts level with the highlighted region, which renderConnector
   measures and writes to --rcle-copy-offset. */
.rcle[data-rcle-exploded] .rcle__carousel {
  position: relative;
  display: grid;
  align-content: start;
  align-self: stretch;
  padding-top: var(--rcle-copy-offset, 0px);
  min-width: 0;
  min-height: 0;
  transition: padding-top 220ms cubic-bezier(0.2, 0.9, 0.35, 1);
}

/* The explanation is text on the background, not a surface. No card,
   border, radius or shadow: the live product is the only object on
   screen, and hierarchy here comes from type size and space alone. */
.rcle[data-rcle-exploded] .rcle__slide {
  position: relative;
  z-index: 2;
  grid-column: 1;
  grid-row: 1;
  min-width: 0;
  min-height: 0;
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
  color: #FFFFFF;
  opacity: 1;
  transform: translateX(0);
  transition:
    opacity 220ms ease,
    transform 220ms cubic-bezier(0.2, 0.9, 0.35, 1);
}

.rcle[data-rcle-exploded] .rcle__slide[hidden] {
  display: none;
}

/* Subtle horizontal enter animations. The active slide picks up a
 * direction class in goToSlide() based on whether the user went
 * forward or backward, then the class is removed on transitionend so
 * the resting state is idempotent. */
.rcle[data-rcle-exploded] .rcle__slide--enter-next {
  animation: rcle-slide-in-right 240ms cubic-bezier(0.2, 0.9, 0.35, 1) both;
}

.rcle[data-rcle-exploded] .rcle__slide--enter-prev {
  animation: rcle-slide-in-left 240ms cubic-bezier(0.2, 0.9, 0.35, 1) both;
}

@keyframes rcle-slide-in-right {
  from { opacity: 0; transform: translateX(18px); }
  to   { opacity: 1; transform: translateX(0); }
}

@keyframes rcle-slide-in-left {
  from { opacity: 0; transform: translateX(-18px); }
  to   { opacity: 1; transform: translateX(0); }
}

@media (prefers-reduced-motion: reduce) {
  .rcle[data-rcle-exploded] .rcle__slide,
  .rcle[data-rcle-exploded] .rcle__slide--enter-next,
  .rcle[data-rcle-exploded] .rcle__slide--enter-prev {
    animation: none;
    transition: none;
  }
}

/* Small red numbered badge (annotation number). This is the only
 * ADS-red element inside the explanation card body per the 2026-08-04
 * brief: heading and copy stay in the neutral card text color. */
/* Step number. No box: the spec asks for the numeral alone with a short
   red hairline under it, so the rule is drawn with ::after rather than a
   border on a container. */
.rcle[data-rcle-exploded] .rcle__step {
  margin: 0 0 18px;
  color: var(--rcle-annotation-accent, #C73945);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.08em;
  line-height: 1;
}

.rcle[data-rcle-exploded] .rcle__step::after {
  content: "";
  display: block;
  width: 28px;
  height: 2px;
  margin-top: 10px;
  background: var(--rcle-annotation-accent, #C73945);
}

.rcle[data-rcle-exploded] .rcle__slide-heading {
  margin: 0 0 14px;
  color: #FFFFFF;
  font-size: clamp(28px, 2.1vw, 34px);
  font-weight: 600;
  line-height: 1.18;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.rcle[data-rcle-exploded] .rcle__slide-copy {
  margin: 0 0 28px;
  /* Short measure keeps this readable from the back of a room. */
  max-width: 30ch;
  color: rgba(255, 255, 255, 0.92);
  font-size: clamp(18px, 1.35vw, 21px);
  font-weight: 400;
  line-height: 1.45;
}

/* Three supporting facts. A definition list is the honest structure for
   label-and-value pairs, but it is styled as prose lines rather than
   rows so it never reads as a table. */
/* The closing line sits under the facts, quieter than them, separated by
   a rule rather than a box. */
/* The Edit rate card modal while it is being presented. It keeps its own
   scrim, panel and footer; only its containing block changes, from the
   viewport to the product frame, so it reads as a modal over this rate
   card rather than over the presentation. */
.rcle[data-rcle-exploded] .rcle__product-frame .modal--rc-create {
  position: absolute;
  inset: 0;
  z-index: 5;
}

/* Sized to the frame rather than the viewport, so the whole modal from
   header to footer is visible without the presenter scrolling. */
.rcle[data-rcle-exploded] .rcle__product-frame .modal--rc-create .modal__panel {
  /* Take the whole frame height that is going spare. The modal is taller
     than the frame at every supported size, so every pixel here is one
     fewer the presenter has to scroll for. */
  height: calc(100% - 32px);
  max-height: calc(100% - 32px);
  max-width: calc(100% - 32px);
}

.rcle[data-rcle-exploded] .rcle__slide-note {
  margin: 26px 0 0;
  padding-top: 16px;
  max-width: 30ch;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  /* Quieter than the facts through colour, not size: this is a sentence
     an audience reads, so it stays on the readable floor. */
  color: rgba(255, 255, 255, 0.5);
  font-size: 16px;
  line-height: 1.45;
}

/* One quiet line under the facts, for the slide whose point is an
   interaction rather than a field. */
.rcle[data-rcle-exploded] .rcle__slide-support {
  margin: 20px 0 0;
  max-width: 34ch;
  color: rgba(255, 255, 255, 0.72);
  font-size: 16px;
  line-height: 1.5;
}

.rcle[data-rcle-exploded] .rcle__facts {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin: 0;
}

.rcle[data-rcle-exploded] .rcle__fact {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
}

.rcle[data-rcle-exploded] .rcle__fact dt {
  margin: 0;
  color: #FFFFFF;
  font-size: clamp(16px, 1.1vw, 17px);
  font-weight: 500;
  line-height: 1.4;
}

.rcle[data-rcle-exploded] .rcle__fact dt::after {
  /* Written here rather than in the markup so the separator never ends
     up in the accessible name or in a copy-paste of the sentence. */
  content: "";
  display: inline-block;
  width: 14px;
  height: 1px;
  margin: 0 0 0 8px;
  vertical-align: middle;
  background: rgba(255, 255, 255, 0.38);
}

.rcle[data-rcle-exploded] .rcle__fact dd {
  margin: 0;
  color: rgba(255, 255, 255, 0.66);
  font-size: clamp(16px, 1.1vw, 17px);
  font-weight: 400;
  line-height: 1.4;
}

.rcle[data-rcle-exploded] .rcle__carousel-nav {
  display: inline-flex;
  align-items: center;
  justify-self: center;
  gap: 20px;
  padding: 0;
  /* Its own grid row, so it reserves space rather than floating over
     the composition however short the viewport gets. */
  margin-top: clamp(12px, 1.6vh, 20px);
  color: var(--rcle-surface);
}

.rcle[data-rcle-exploded] .rcle__carousel-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 50%;
  background: transparent;
  color: var(--rcle-surface);
  cursor: pointer;
  transition:
    background-color 160ms ease,
    border-color 160ms ease,
    color 160ms ease,
    opacity 160ms ease;
}

.rcle[data-rcle-exploded] .rcle__carousel-btn:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.10);
  border-color: rgba(255, 255, 255, 0.55);
}

.rcle[data-rcle-exploded] .rcle__carousel-btn:focus-visible {
  outline: 2px solid var(--rcle-focus, #A8B4FF);
  outline-offset: 2px;
}

.rcle[data-rcle-exploded] .rcle__carousel-btn:disabled,
.rcle[data-rcle-exploded] .rcle__carousel-btn[aria-disabled="true"] {
  border-color: rgba(255, 255, 255, 0.16);
  color: rgba(255, 255, 255, 0.34);
  cursor: not-allowed;
  opacity: 0.55;
}

.rcle[data-rcle-exploded] .rcle__carousel-indicator {
  min-width: 56px;
  color: var(--rcle-surface);
  font-family: "Open Sans", sans-serif;
  font-size: 16px;
  font-weight: 500;
  line-height: 1;
  text-align: center;
  letter-spacing: 0.01em;
}

.rcle[data-rcle-exploded] .rcle__callouts {
  position: absolute;
  inset: 0;
  z-index: 4;
  overflow: visible;
}

/* ADS brand red for the annotation system (2026-08-04 carousel
 * revision). Only the numbered badge inside the explanation card,
 * the ring around the target, the connector line, and the numbered
 * marker circle carry the red so it reads as one on-brand annotation
 * system without leaking into headings, body copy, or product UI. */
.rcle[data-rcle-exploded] {
  --rcle-annotation-accent: #C73945;
  --rcle-annotation-accent-soft: rgba(199, 57, 69, 0.55);
  --rcle-annotation-accent-line: rgba(199, 57, 69, 0.9);
}

/* Spotlight fill. Navy at 42% over the white product reads as roughly
   60% opacity on the parts that are not being explained, while the
   focus region keeps the product's own full contrast. */
.rcle[data-rcle-exploded] .rcle__callouts .scrim {
  fill: rgba(2, 0, 36, 0.42);
  pointer-events: none;
}

.rcle[data-rcle-exploded] .rcle__callouts .source-ring {
  fill: none;
  stroke: var(--rcle-annotation-accent);
  stroke-width: 1.5px;
  stroke-opacity: 0.8;
  rx: var(--ads-radius-sm, 6px);
  ry: var(--ads-radius-sm, 6px);
  vector-effect: non-scaling-stroke;
}

/* Secondary ring (slides 2 + 3): drawn slightly quieter than the
 * primary form ring so the add-or-edit form still reads as the
 * leading target, while the surrounding table ring makes the
 * "existing records" context obvious. Same ADS-red hue, dashed so
 * it visually differentiates from the primary ring at a glance
 * without introducing a second color. */
.rcle[data-rcle-exploded] .rcle__callouts .source-ring--secondary {
  stroke-opacity: 0.75;
  stroke-width: 1.5px;
  stroke-dasharray: 6 4;
}

.rcle[data-rcle-exploded] .rcle__callouts .connector {
  fill: none;
  stroke: var(--rcle-annotation-accent);
  stroke-width: 1.5px;
  stroke-linecap: round;
  stroke-linejoin: miter;
  stroke-opacity: 0.9;
  vector-effect: non-scaling-stroke;
}

.rcle[data-rcle-exploded] .rcle__callouts .connector--secondary {
  stroke-opacity: 0.8;
  stroke-width: 1.5px;
  stroke-dasharray: 6 4;
}

.rcle[data-rcle-exploded] .rcle__callouts .marker {
  fill: var(--rcle-annotation-accent);
  stroke: var(--rcle-navy);
  stroke-width: 1px;
}

.rcle[data-rcle-exploded] .rcle__callouts .number {
  fill: var(--rcle-surface);
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  font-weight: 700;
}

/* The exploded surfaces used to nudge the mark to their own corner inset
 * and dim it differently from the deck. Both now come from the shell. */

@media (max-width: 1100px) {
  .rcle[data-rcle-exploded] .rcle__panel {
    padding-right: 36px;
    padding-left: 36px;
  }

  /* Narrower viewports take width from the explanation first, never
     from the product, so table text stays readable. */
  .rcle[data-rcle-exploded] .rcle__diagram {
    grid-template-columns: minmax(0, 72fr) minmax(232px, 24fr);
    gap: 32px;
  }

  .rcle[data-rcle-exploded] .rcle__slide-copy {
    max-width: none;
  }
}

@media (max-height: 820px) {
  .rcle[data-rcle-exploded] .rcle__step {
    margin-bottom: 14px;
  }

  .rcle[data-rcle-exploded] .rcle__slide-copy {
    margin-bottom: 22px;
  }

  .rcle[data-rcle-exploded] .rcle__facts {
    gap: 11px;
  }
}

@media (max-height: 760px) {
  .rcle[data-rcle-exploded] .rcle__panel {
    gap: 10px;
    padding-top: 18px;
    padding-bottom: 24px;
  }

  .rcle[data-rcle-exploded] .rcle__product-frame {
    max-height: 100%;
  }

  .rcle[data-rcle-exploded] .rcle__step {
    margin-bottom: 12px;
  }

  .rcle[data-rcle-exploded] .rcle__slide-copy {
    margin-bottom: 18px;
  }

  .rcle[data-rcle-exploded] .rcle__facts {
    gap: 9px;
  }
}

/* -----------------------------------------------------------------
 * Header actions + view toggle (2026-08-04 previous-design brief)
 * ----------------------------------------------------------------- */

/* Right-side group in the header: [Previous design | Close]. Kept
 * as inline-flex so the close button retains its 40 x 40 hit area
 * and the toggle sits 14 px to its left (within the 12-16 px range
 * specified in the brief). */
.rcle__header-actions {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 14px;
}

/* Subtle secondary "Previous design" / "Current design" button.
 * Visually quiet against the dark navy panel: 1 px outline over a
 * very low-emphasis tinted background. No ADS red (reserved for
 * annotation numbers + connector lines only). Min 16 px label. */
.rcle__view-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 36px;
  margin: 0;
  padding: 0 16px;
  border: 1px solid rgba(255, 255, 255, 0.30);
  border-radius: var(--ads-radius-sm, 6px);
  background: rgba(255, 255, 255, 0.05);
  color: var(--rcle-surface);
  font-family: "Open Sans", sans-serif;
  font-size: 16px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0;
  cursor: pointer;
  transition:
    background-color 160ms ease,
    border-color 160ms ease,
    transform 100ms ease;
}

.rcle__view-toggle:hover {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.55);
}

.rcle__view-toggle:active {
  background: rgba(255, 255, 255, 0.16);
  transform: scale(0.98);
}

.rcle__view-toggle:focus-visible {
  outline: 2px solid var(--rcle-focus, #A8B4FF);
  outline-offset: 2px;
}

.rcle__view-toggle[hidden] { display: none; }

/* -----------------------------------------------------------------
 * Grid placement for the exploded panel body:
 *   row 1 -> header (auto)
 *   row 2 -> diagram (current view) OR previous section
 *   row 3 -> carousel nav (current view only)
 * setView() toggles hidden on the row-2 siblings; the nav is hidden
 * in the previous view too, so row 3 collapses to zero height there.
 * ----------------------------------------------------------------- */
.rcle[data-rcle-exploded] .rcle__diagram,
.rcle[data-rcle-exploded] .rcle__previous {
  grid-row: 2;
}

.rcle[data-rcle-exploded] .rcle__carousel-nav {
  grid-row: 3;
}

/* -----------------------------------------------------------------
 * Previous-design view
 * -----------------------------------------------------------------
 * Full-body slot: a single white scrollable frame that hosts the
 * reconstructed prototype. The prototype renders at natural 1440 px
 * width and is proportionally zoomed by --rcle-prev-scale (computed
 * in fitPreviousPrototype() in app.js). When the scaled height
 * exceeds the frame height, the frame scrolls vertically so no
 * important content is cropped.
 * ----------------------------------------------------------------- */
.rcle__previous {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}

.rcle__previous[hidden] { display: none; }

.rcle__previous-frame {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  /*
   * Reserve a stable gutter for the scrollbar so it never overlays the
   * prototype's top-right controls (bell, avatar). Firefox / Safari fall
   * back to the plain thin scrollbar; Chromium honors the WebKit hook
   * below to render a slim, in-gutter track.
   */
  overflow: auto;
  scrollbar-gutter: stable;
  scrollbar-width: thin;
  scrollbar-color: rgba(15, 18, 20, 0.35) transparent;
  border: 1px solid var(--rcle-card-divider, rgba(15, 18, 20, 0.10));
  border-radius: var(--ads-radius-card, 16px);
  background: #ffffff;
  box-shadow: var(--ads-shadow-card-high, var(--rcle-shadow));
  outline: none;
}

.rcle__previous-frame::-webkit-scrollbar {
  width: 10px;
  height: 10px;
  background: transparent;
}

.rcle__previous-frame::-webkit-scrollbar-thumb {
  background: rgba(15, 18, 20, 0.28);
  background-clip: padding-box;
  border: 2px solid transparent;
  border-radius: 6px;
}

.rcle__previous-frame::-webkit-scrollbar-thumb:hover {
  background: rgba(15, 18, 20, 0.45);
  background-clip: padding-box;
  border: 2px solid transparent;
}

.rcle__previous-frame:focus-visible {
  outline: 2px solid var(--rcle-focus, #A8B4FF);
  outline-offset: 3px;
}

.rcle__previous-viewport {
  /*
   * Sized by the zoomed prototype width, but always at least as
   * wide as the frame so the flex centering below has room to
   * work. On wider viewports (e.g. 1920 x 1080) the prototype is
   * capped at its natural 1440 px width and centered horizontally
   * so the extra frame area does not become a large empty margin
   * on one side.
   */
  min-width: 100%;
  min-height: 100%;
  display: flex;
  align-items: flex-start;
  justify-content: center;
}

/* -----------------------------------------------------------------
 * Reconstructed prototype (Figma node 523:8884, file Klc8JpICWSL...).
 * Natural size is 1440 px wide; height follows the content stack.
 * --rcle-prev-scale is a zoom factor (layout-aware) set by
 * fitPreviousPrototype in app.js so the prototype shrinks
 * proportionally to fit the frame width without distortion or
 * cropping. All descendant rules are scoped under
 * .rcle__previous-prototype to keep them isolated from the live
 * product UI on the current-view side.
 *
 * Colors are the EDL palette values from the Figma file:
 *   Indigo/70 #3611c8, Indigo/15 #cfcfff, Indigo/10 #e6e6ff
 *   Gray/95 #171c20, Gray/60 #6a8095, Gray/50 #8498a9,
 *   Gray/40 #9eb0bd, Gray/20 #d4dee3, Gray/05 #f7f9fa
 * Fonts: MultiplaneTWDC Display (titles), InspireTWDC (nav / step),
 * Open Sans (body / form labels).
 * ----------------------------------------------------------------- */
.rcle__previous-prototype {
  width: 1440px;
  zoom: var(--rcle-prev-scale, 1);
  background: #ffffff;
  color: #171c20;
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  line-height: 20px;
}

/*
 * Global nav bar. Figma: node I523:8891;6200:66075, height 48 px,
 * background Indigo/70 (#3611c8). Left cluster is padded 48 px from
 * the surface edge; right cluster is padded 28 px.
 */
.rcle__previous-prototype .prev-nav {
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  height: 48px;
  padding-right: 28px;
  background: #3611c8;
  color: #ffffff;
}

.rcle__previous-prototype .prev-nav__left {
  display: flex;
  align-items: stretch;
  height: 100%;
}

.rcle__previous-prototype .prev-nav__logomark {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  height: 100%;
  padding: 0 16px 0 48px;
}

.rcle__previous-prototype .prev-nav__logo {
  display: block;
  width: 144px;
  height: 26px;
}

.rcle__previous-prototype .prev-nav__divider {
  display: inline-block;
  width: 1px;
  height: 30px;
  background: rgba(255, 255, 255, 0.32);
}

.rcle__previous-prototype .prev-nav__item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 100%;
  padding: 0 16px;
  color: #ffffff;
  font-family: "InspireTWDC", "Open Sans", sans-serif;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0;
  white-space: nowrap;
}

.rcle__previous-prototype .prev-nav__item--current {
  background: rgba(255, 255, 255, 0.12);
}

.rcle__previous-prototype .prev-nav__caret {
  color: #ffffff;
  opacity: 0.9;
}

.rcle__previous-prototype .prev-nav__right {
  display: flex;
  align-items: center;
  gap: 24px;
}

.rcle__previous-prototype .prev-nav__bell {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  color: #ffffff;
}

.rcle__previous-prototype .prev-nav__bell img {
  display: block;
  width: 16px;
  height: 16px;
}

.rcle__previous-prototype .prev-nav__avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: #ffffff;
  overflow: hidden;
}

.rcle__previous-prototype .prev-nav__avatar img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/*
 * Page surface. Figma: node 523:8892, 826 px tall, 1440 px wide,
 * background Gray/05 (#f7f9fa) with a 1 px Gray/20 border-top.
 * The stepper column is pinned via absolute positioning at the
 * Figma-exact coordinates (left:31, top:49); the body column uses
 * natural flow with padding-left:153 to align with the cards.
 */
.rcle__previous-prototype .prev-surface {
  position: relative;
  min-height: 826px;
  padding: 60px 59px 60px 59px;
  background: #f7f9fa;
  border-top: 1px solid #d4dee3;
}

.rcle__previous-prototype .prev-stepper {
  position: absolute;
  left: 31px;
  top: 49px;
  display: flex;
  flex-direction: column;
  width: 122px;
}

.rcle__previous-prototype .prev-step {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.rcle__previous-prototype .prev-step__row {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  height: 23px;
}

.rcle__previous-prototype .prev-step__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border: 1px solid #8498a9;
  border-radius: 50%;
  color: #8498a9;
  font-family: "InspireTWDC", "Open Sans", sans-serif;
  font-weight: 700;
  font-size: 14px;
  line-height: 1;
  letter-spacing: -0.01em;
}

.rcle__previous-prototype .prev-step__num--active {
  border-color: transparent;
  background: #3611c8;
  color: #ffffff;
}

.rcle__previous-prototype .prev-step__label {
  font-family: "InspireTWDC", "Open Sans", sans-serif;
  font-weight: 500;
  font-size: 18px;
  line-height: 21px;
  color: #171c20;
}

.rcle__previous-prototype .prev-step__chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: fit-content;
  height: 20px;
  margin-left: 33px;
  padding: 0 8px;
  border: 1px solid #d4dee3;
  border-radius: 100px;
  color: #6a8095;
  font-family: "InspireTWDC", "Open Sans", sans-serif;
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.04em;
  text-transform: none;
}

.rcle__previous-prototype .prev-step__chip--active {
  background: #e6e6ff;
  border-color: #cfcfff;
  color: #3611c8;
}

.rcle__previous-prototype .prev-step__line {
  display: block;
  width: 1px;
  height: 44px;
  margin: 12px 0 12px 11.5px;
  background: #d4dee3;
}

/*
 * Body column. Contains the Rate Card Manager back-crumb, page
 * header row (title / subtitle + right-side action buttons), and
 * the stacked section cards. Body sits at the surface's padding
 * boundary (x:59); the crumb, header, and cards each shift
 * inward by 94 px so they align with the Figma cards column at
 * x:153, keeping them clear of the absolute-positioned stepper
 * that occupies x:31 to about x:120.
 */
.rcle__previous-prototype .prev-body {
  min-width: 0;
  padding-left: 94px;
}

.rcle__previous-prototype .prev-crumb {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px;
  color: #3611c8;
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
}

.rcle__previous-prototype .prev-crumb svg {
  color: currentColor;
}

.rcle__previous-prototype .prev-page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  margin: 24px 0 40px;
}

.rcle__previous-prototype .prev-page-header__titles {
  min-width: 0;
  max-width: 800px;
  padding-top: 2px;
}

.rcle__previous-prototype .prev-page-header__title {
  margin: 0;
  font-family: "MultiplaneTWDC Display", "InspireTWDC", "Open Sans", sans-serif;
  font-weight: 500;
  font-size: 28px;
  line-height: 38px;
  color: #171c20;
}

.rcle__previous-prototype .prev-page-header__sub {
  margin: 8px 0 0;
  font-family: "InspireTWDC", "Open Sans", sans-serif;
  font-size: 16px;
  line-height: 22px;
  color: #171c20;
}

.rcle__previous-prototype .prev-page-header__actions {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  flex: 0 0 auto;
  padding-top: 4px;
}

/*
 * Buttons. All three are 40 px tall (Figma: `size={40}`). Demoted =
 * indigo-on-transparent (Save as Draft). Standard/secondary = indigo
 * outline on white (Save and Create New LINE). Promoted/primary =
 * indigo fill with white text (Save and Continue to PREM). All
 * copy is preserved verbatim from the Figma frame.
 */
.rcle__previous-prototype .prev-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 40px;
  padding: 0 16px;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
}

.rcle__previous-prototype .prev-btn--demoted {
  padding: 0 10px;
  color: #3611c8;
}

.rcle__previous-prototype .prev-btn--secondary {
  color: #3611c8;
  border-color: #3611c8;
  background: #ffffff;
}

.rcle__previous-prototype .prev-btn--primary {
  color: #ffffff;
  background: #3611c8;
}

/*
 * Section cards. Figma: node 523:10002. Column of 3 white cards,
 * 30 px gap. Each card is drop-shadowed (0px 0px 2px rgba(23,29,35,0.13)),
 * 16 px radius, 32 px vertical + 24 px horizontal padding, and lays
 * its heading + description + field rows out with 16 px gap.
 */
.rcle__previous-prototype .prev-cards {
  display: flex;
  flex-direction: column;
  gap: 30px;
  min-width: 0;
}

.rcle__previous-prototype .prev-card {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 32px 24px;
  border-radius: 16px;
  background: #ffffff;
  box-shadow: 0 0 2px rgba(23, 29, 35, 0.13);
}

.rcle__previous-prototype .prev-card__head {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.rcle__previous-prototype .prev-card__title {
  margin: 0;
  font-family: "MultiplaneTWDC Display", "InspireTWDC", "Open Sans", sans-serif;
  font-weight: 500;
  font-size: 24px;
  line-height: 28px;
  color: #161c1e;
  letter-spacing: 0;
}

.rcle__previous-prototype .prev-card__sub {
  margin: 0;
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  line-height: 20px;
  color: #161c1e;
}

.rcle__previous-prototype .prev-card__id {
  margin: 0;
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  line-height: 18px;
}

.rcle__previous-prototype .prev-card__id-key {
  font-weight: 700;
  color: #3b4347;
  font-size: 14px;
}

.rcle__previous-prototype .prev-card__id-val {
  margin-left: 4px;
  color: rgba(15, 18, 20, 0.30);
  font-weight: 700;
  font-size: 14px;
}

/*
 * Field rows. Figma uses flex containers with per-field width
 * variants ("400 px", "619 px", or flex-1). To stay faithful, each
 * row is a flex container and fields carry width modifiers when
 * fixed.
 */
.rcle__previous-prototype .prev-row {
  display: flex;
  align-items: flex-start;
  gap: 32px;
  min-width: 0;
}

.rcle__previous-prototype .prev-field {
  display: flex;
  flex: 1 1 0;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

.rcle__previous-prototype .prev-field--400 {
  flex: 0 0 400px;
}

.rcle__previous-prototype .prev-field--619 {
  flex: 0 0 619px;
}

.rcle__previous-prototype .prev-field__label {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: #3b4347;
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  font-weight: 600;
  line-height: 18px;
}

.rcle__previous-prototype .prev-field__info {
  flex: 0 0 auto;
  color: #6a8095;
}

.rcle__previous-prototype .prev-input {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 36px;
  padding: 8px 12px;
  border: 1px solid rgba(15, 18, 20, 0.20);
  border-radius: 6px;
  background: #ffffff;
}

.rcle__previous-prototype .prev-input__placeholder {
  flex: 1 1 auto;
  min-width: 0;
  color: rgba(15, 18, 20, 0.30);
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  line-height: 20px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rcle__previous-prototype .prev-input__value {
  flex: 1 1 auto;
  min-width: 0;
  color: #1e2528;
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  line-height: 20px;
}

.rcle__previous-prototype .prev-input__caret {
  flex: 0 0 auto;
  color: #171c20;
}

.rcle__previous-prototype .prev-input__cal {
  flex: 0 0 auto;
  color: #171c20;
}

/* Rate Card list explainer, hosted by the same presentation overlay. */
.rcle[data-rcle-mode="list"] {
  --rcle-list-accent: #C73945;
  --rcle-list-accent-soft: rgba(199, 57, 69, 0.55);
  --rcle-list-accent-line: rgba(199, 57, 69, 0.9);
}

/* No navigation row in list mode until a step reveal needs one; the
   panel adds it back through the same grid the detail carousel uses. */
.rcle[data-rcle-mode="list"] .rcle__panel {
  grid-template-rows: auto minmax(0, 1fr) auto;
}

/* Product left, narration right. The product is the hero at roughly
   three quarters of the content width, and both tracks are minmax(0,..)
   so a wide table cannot push the grid past the viewport. */
.rcle-list {
  grid-column: 1;
  /* The callout SVG is inset:0 against this element, so its coordinates
     and the rects measured from it have to share an origin. Without a
     positioned ancestor here the annotation was drawn against the whole
     overlay and landed a panel's padding away from its target. */
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 74fr) minmax(200px, 22fr);
  grid-template-rows: minmax(0, 1fr);
  gap: clamp(32px, 2.6vw, 40px);
  overflow: hidden;
  min-width: 0;
  min-height: 0;
}

.rcle-list[hidden] {
  display: none;
}

.rcle-list__product {
  position: relative;
  display: grid;
  /* The frame takes its own height from the content rather than filling
     the row, and is centred in whatever space is left, so removing the
     white band below the footer does not just move it outside the
     frame as navy. */
  grid-template-rows: auto;
  align-content: center;
  overflow: hidden;
  min-width: 0;
  min-height: 0;
}

.rcle-list__frame {
  width: 100%;
  /* fitListRoot() measures the rendered list and writes this. The
     fallback only applies for the frame before the first measurement. */
  height: var(--rcle-list-frame-h, 100%);
  max-height: 100%;
  overflow: hidden;
  border: 1px solid var(--rcle-border);
  border-radius: var(--ads-radius-card, 16px);
  background: var(--rcle-card-surface, #FFFFFF);
  box-shadow: var(--ads-shadow-card-high, var(--rcle-shadow));
}

.rcle-list__viewport,
.rcle-list__live-slot {
  width: 100%;
  height: 100%;
  min-height: 0;
  overflow: hidden;
}

/* The list page is laid out for a full browser window, so it is scaled
   to fit the frame. fitListRoot() measures the frame and writes the
   factor; the width below is the page's natural design width. */
body.rcle-list-open .rcle-list__live-slot > [data-page="list"] {
  position: relative;
  left: 50%;
  width: 1360px;
  /* The product route caps every page at the window width. Here the page
   * is a thing being scaled, not a thing being fitted, so the cap has to
   * come off: with it on, the list laid out at the frame width and its
   * right-hand columns spilled past the frame and were clipped instead of
   * shrinking with everything else. */
  max-width: none;
  transform: translateX(-50%) scale(var(--rcle-list-scale, 1));
  transform-origin: top center;
}

body.rcle-list-open
  .rcle-list__live-slot
  > [data-page="list"]
  .surface__inner
  > .page__header {
  opacity: 0.52;
}

/* Trim the page's own outer padding while it is being presented. That
   padding exists to breathe inside a browser window; inside the frame it
   only pushed the table away from the edges and made the product look
   smaller than the space allowed. */
body.rcle-list-open .rcle-list__live-slot > [data-page="list"] {
  padding: 0;
}

body.rcle-list-open .rcle-list__live-slot .page__inner {
  max-width: none;
  padding: 20px 24px;
}

body.rcle-list-open .rcle-list__live-slot .table,
body.rcle-list-open .rcle-list__live-slot .card {
  box-shadow: none;
}

/* ---- Narration rail ------------------------------------------------
   Text on the background. No card, border, radius or shadow: hierarchy
   is type size, weight and space, and the reading order down the column
   is the story. */
.rcle-list__rail {
  display: flex;
  flex-direction: column;
  /* Safe centring: once the three steps are taller than the rail, plain
     centring pushes the first one off the top where it cannot be read or
     scrolled back to. Safe centring falls back to the start instead. */
  justify-content: safe center;
  gap: clamp(32px, 3.4vh, 40px);
  min-width: 0;
  /* Below the supported presentation sizes the three steps stop fitting
     however tight the rhythm gets. Scrolling keeps every step reachable
     instead of cutting the last one off. At 1280x720 and above the rail
     never overflows, so the scrollbar is never seen in normal use. */
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.28) transparent;
}

.rcle-list__step {
  max-width: 260px;
  /* Steps that are not active recede rather than disappear, so the whole
     three-step story stays readable while one of them is being made. */
  opacity: 0.42;
  transition: opacity 220ms ease;
}

.rcle-list__step.is-active {
  opacity: 1;
}

.rcle-list__step-num {
  margin: 0 0 10px;
  color: var(--rcle-list-accent);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.08em;
  line-height: 1;
}

.rcle-list__step h3 {
  margin: 0 0 9px;
  color: #FFFFFF;
  font-size: clamp(21px, 1.5vw, 24px);
  font-weight: 600;
  line-height: 1.22;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.rcle-list__step p {
  margin: 0;
  color: rgba(255, 255, 255, 0.66);
  font-size: clamp(16px, 1.15vw, 18px);
  font-weight: 400;
  line-height: 1.45;
}

.rcle-list__takeaway {
  max-width: 260px;
  margin: 0;
  padding-top: clamp(12px, 1.6vh, 20px);
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  color: rgba(255, 255, 255, 0.5);
  font-size: 15px;
  font-weight: 400;
  line-height: 1.45;
}

/* ---- Annotation ----------------------------------------------------
   One ring and one leader for the active step only, so no two lines can
   cross and nothing is drawn over a label or a value. */
.rcle-list__callouts {
  position: absolute;
  inset: 0;
  z-index: 4;
  overflow: visible;
  pointer-events: none;
}

.rcle-list__callouts .scrim {
  fill: rgba(2, 0, 36, 0.34);
}

.rcle-list__callouts .source-ring {
  fill: rgba(199, 57, 69, 0.06);
  stroke: var(--rcle-list-accent);
  stroke-width: 1.5px;
  stroke-opacity: 0.85;
  vector-effect: non-scaling-stroke;
}

.rcle-list__callouts .connector {
  fill: none;
  stroke: var(--rcle-list-accent-line);
  stroke-width: 1.5px;
  vector-effect: non-scaling-stroke;
}

@media (max-width: 1180px) {
  /* Width comes off the narration first; the product never shrinks to
     buy room for text. */
  .rcle-list {
    grid-template-columns: minmax(0, 78fr) minmax(184px, 20fr);
    gap: 28px;
  }

  .rcle-list__step,
  .rcle-list__takeaway {
    max-width: none;
  }
}

@media (max-height: 800px) {
  .rcle-list__rail {
    gap: 24px;
  }

  .rcle-list__step h3 {
    font-size: 21px;
  }

  .rcle-list__takeaway {
    display: none;
  }
}

/* On a short laptop the canvas is height-bound, so the narration column is
   both shorter and narrower and its three steps stop fitting. Tighten the
   rhythm between and inside the steps. Nothing is reworded, resized away
   or dropped: this only closes the space the steps sit in. */
@media (max-height: 800px) {
  .rcle-list__rail {
    gap: 16px;
  }

  .rcle-list__step-num {
    margin-bottom: 5px;
  }

  .rcle-list__step h3 {
    margin-bottom: 5px;
    line-height: 1.16;
  }

  .rcle-list__step p {
    line-height: 1.34;
  }
}

/* ============================================================================
 *  BUYER COMPLEXITY MODAL (BCM)  -  v1.2 only
 *
 *  Educational modal that opens from the "Buyer" tile on the "Why Pricing
 *  Gets Complicated Before Planning" Atlas slide. Shows a side-by-side
 *  advertiser mix comparison to illustrate why a single agency can carry
 *  different pricing implications year over year. Only visible / reachable
 *  when body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) is active; the underlying tile stays
 *  clickable-looking only under that version. v1.0 / v1.1 users see the
 *  same static tile they always have.
 *
 *  DOM contract (see index.html):
 *    .bcm                            fixed-position overlay root
 *      .bcm__scrim                   dark backdrop, click closes
 *      .bcm__panel                   centered white card
 *        .bcm__header                title + close X
 *        .bcm__body                  two-column brands + note
 *          .bcm__cols
 *            .bcm__col
 *              .bcm__col-title
 *              .bcm__brands (ul)
 *                .bcm__brand
 *                  .bcm__brand-mark  (inline SVG badge)
 *                  .bcm__brand-name
 *          .bcm__note
 *
 *  Interaction wiring lives in initBuyerComplexityModal() in app.js.
 * ========================================================================== */

/* v1.2-only hoverable Buyer tile.
 *
 * IMPORTANT: .atlas-stage sets pointer-events: none so the outer
 * .atlas-stage-wrap can catch every click for the deck's advance()
 * handler. That also means individual tiles never receive :hover or
 * mouseenter events by default. Re-enabling pointer-events on this
 * one tile lets the hover popover fire correctly. Clicks still bubble
 * up through the DOM (pointer-events: none blocks hit-testing, NOT
 * event bubbling) so the atlas-stage-wrap click handler continues to
 * fire and click-to-next continues to advance the slide. Cursor stays
 * "default" - the tile is informational, not clickable. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide--why-pricing .wpc__card--buyer {
  pointer-events: auto;
  cursor: default;
  transition: transform 160ms ease, border-color 160ms ease, box-shadow 160ms ease;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide--why-pricing .wpc__card--buyer:hover,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide--why-pricing .wpc__card--buyer:focus-visible {
  outline: none;
  border-color: rgba(255, 122, 90, 0.55);        /* subtle coral halo, matches the WPC coral accent */
  box-shadow:
    0 12px 32px rgba(0, 0, 0, 0.42),
    0 0 24px rgba(255, 122, 90, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.12);
  transform: translateY(-1px);
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide--why-pricing .wpc__card--buyer:active {
  transform: translateY(0);
}

/* --- overlay root ---------------------------------------------------------
 *
 * v1.2 hover popover model. The .bcm wrapper is a passive
 * viewport-sized container that lets clicks fall through to the
 * atlas deck's wrap-level click handler (page click-to-next).
 * The .bcm__panel underneath re-enables pointer-events: auto so
 * mouse-hover / focus can keep the popover open. */
.bcm {
  position: fixed;
  inset: 0;
  z-index: 500;               /* above the atlas deck stage */
  pointer-events: none;       /* clicks pass through to atlas wrap */
  font-family: "InspireTWDC", "Open Sans", "DM Sans", Inter, sans-serif;
}
.bcm[hidden] { display: none; }

/* --- panel ---------------------------------------------------------------- */
/* JS positions the panel via tile.getBoundingClientRect(). The panel
 * uses position: fixed with computed top/left values. Width is clamped
 * against the viewport so it can never overflow the visible page. */
.bcm__panel {
  position: fixed;            /* top/left set by JS */
  pointer-events: auto;       /* re-enable interaction inside the panel */
  /* 2026-07-09 real-logo swap: bumped from 520 -> 620 so the wider
   * Coca-Cola / Amazon / Airbnb wordmark SVGs from Wikimedia have
   * enough horizontal room to letterbox at a legible size. Still
   * narrower than the DCM panel (620 hard) so the Buyer popover
   * keeps its own visual identity. */
  width: min(620px, calc(100% - 32px));
  max-height: calc(100vh - 32px);
  max-height: calc(100dvh - 32px);
  background: #FFFFFF;
  border: 1px solid #E5E7EB;
  border-radius: 16px;
  box-shadow:
    0 24px 60px rgba(0, 0, 0, 0.32),
    0 6px 18px rgba(0, 0, 0, 0.14);
  outline: none;
  display: flex;
  flex-direction: column;
  overflow: hidden;           /* body handles its own overflow */
  /* Subtle fade+lift on open so the popover feels like a hover
   * affordance rather than a modal dialog. */
  animation: bcm-in 160ms ease-out;
}
@keyframes bcm-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0);   }
}

/* --- header --------------------------------------------------------------- */
.bcm__header {
  padding: 18px 20px;
  border-bottom: 1px solid #E5E7EB;
}
.bcm__title {
  margin: 0;
  font-family: "InspireTWDC", "Open Sans", Inter, sans-serif;
  font-weight: 700;
  font-size: 17px;
  line-height: 1.35;
  letter-spacing: -0.005em;
  color: #111827;
}

/* --- body ----------------------------------------------------------------- */
.bcm__body {
  padding: 24px 20px 20px;
  overflow-y: auto;
}
.bcm__cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 24px;
  row-gap: 8px;
}
.bcm__col {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.bcm__col-title {
  margin: 0;
  font-family: "InspireTWDC", "Open Sans", Inter, sans-serif;
  font-weight: 700;
  font-size: 15px;
  line-height: 20px;
  color: #111827;
  text-align: center;
}

/* --- brands row ----------------------------------------------------------- */
.bcm__brands {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  column-gap: 8px;
  row-gap: 6px;
}
.bcm__brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
/* 2026-07-09 real-logo swap.
 * The Buyer modal now references the OFFICIAL Wikimedia Commons SVGs
 * (Coca-Cola script wordmark, Target bullseye+wordmark, LG face
 * mark, Amazon wordmark w/ smile, Airbnb Belo+wordmark). Their
 * native viewBoxes vary from 3.3:1 (wide horizontal wordmark) to
 * 0.75:1 (tall stacked mark), so the old 64x64 circular clip
 * cropped and distorted all of them. The badge is now a
 * full-column-width rectangular letterbox that uses object-fit
 * to preserve each logo's real aspect ratio, keeps the row
 * baseline aligned, and drops the circular chrome + white-on-white
 * borders the fake marks used to need. */
.bcm__brand-mark {
  width: 100%;
  height: 56px;
  min-height: 56px;
  border: 0;
  border-radius: 0;
  background: transparent;
  overflow: visible;
  padding: 4px 6px;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: none;
}
.bcm__brand-mark svg,
.bcm__brand-mark img {
  /* Letterbox: fill the cell but preserve the logo's native aspect
   * ratio so wordmarks (Coca-Cola / Amazon / Airbnb) and tall marks
   * (Target) both render without distortion or cropping. */
  width: 100%;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}
/* No per-brand border overrides: real Wikimedia SVGs carry their own
 * color ink and read cleanly on the modal's white surface. */

/* --- Per-brand visual weight normalization (v1.2 Buyer modal) --------
 * Real brand SVGs have wildly different aspect ratios and different
 * "content bounding box vs viewBox" ratios. object-fit: contain
 * preserves each logo's aspect but that leaves LG's 1:1 disc filling
 * the full 48px cell while Target's 0.75:1 stack shows at 36x48, and
 * leaves Amazon riding high because its Wikimedia viewBox has ~27px of
 * empty space at the bottom (content Y=1..155 inside a 0..182 box).
 * Two surgical tweaks fix both without touching the wordmark logos:
 *   - LG: cap the img at 40x40 so its disc reads at the same visual
 *     footprint as Target's bullseye+wordmark stack (~36x48). Aspect
 *     ratio is preserved (still 1:1, still object-fit: contain).
 *   - Amazon: nudge the img down 3px so the visible "amazon"+smile
 *     pair recenters within the cell instead of hugging the top edge.
 *     Uses transform (not object-position) because the whitespace we
 *     need to compensate for lives inside the SVG's own viewBox, so
 *     shifting object-position within the img box does nothing.
 * Coca-Cola / Target / Airbnb are unchanged. Name-label baseline (from
 * .bcm__brand's 8px flex gap) is unaffected because these tweaks only
 * touch the img inside .bcm__brand-mark, not the mark's cell height. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .bcm__brand-mark--lg img {
  max-height: 40px;
  max-width:  40px;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .bcm__brand-mark--amazon img {
  transform: translateY(3px);
}
.bcm__brand-name {
  font-family: "InspireTWDC", "Open Sans", Inter, sans-serif;
  font-weight: 500;
  font-size: 14px;
  line-height: 20px;
  color: #374151;
  text-align: center;
}

/* --- note (bottom explanation) ------------------------------------------- */
.bcm__note {
  margin-top: 20px;
  padding: 16px 20px;
  border-top: 1px solid #E5E7EB;
  text-align: center;
}
.bcm__note-line {
  margin: 0;
  font-family: "InspireTWDC", "Open Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 1.45;
  color: #111827;
  font-weight: 600;
}
.bcm__note-line + .bcm__note-line {
  margin-top: 6px;
}
.bcm__note-line--muted {
  color: #6B7280;
  font-weight: 400;
}

/* --- narrow-desktop tightening ------------------------------------------- */
@media (max-width: 640px) {
  .bcm__panel { width: calc(100% - 32px); }
  .bcm__cols { grid-template-columns: 1fr; row-gap: 20px; }
  .bcm__brands { column-gap: 12px; }
}

/* ==========================================================================
 * Deal Context hover popover (.bcm--dcm)
 * --------------------------------------------------------------------------
 * Sibling of the Buyer .bcm modal, wired by the same wireHoverPopover()
 * factory in app.js. Shares the .bcm / .bcm__panel / .bcm__header /
 * .bcm__body / .bcm__title chrome (radius, shadow, animation, border) so
 * the two popovers feel like the same component.
 *
 * This modal is sized and typeset larger than the Buyer popover per the
 * "leadership can understand it in 5 seconds" brief: wider panel, 15px
 * body copy at 1.55 line height, and a scannable definition list under
 * the Impact heading (including a plain-language Makegood definition).
 * All overrides are scoped under .bcm--dcm so the Buyer popover keeps
 * its original 520px / 14px typography.
 * ========================================================================== */

/* v1.2-only hoverable Deal Context tile - mirror of the Buyer tile
 * hover treatment above so both trigger surfaces feel identical.
 * Same reason for pointer-events: auto (atlas-stage sets it to none). */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide--why-pricing .wpc__card--dcontext {
  pointer-events: auto;
  cursor: default;
  transition: transform 160ms ease, border-color 160ms ease, box-shadow 160ms ease;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide--why-pricing .wpc__card--dcontext:hover,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide--why-pricing .wpc__card--dcontext:focus-visible {
  outline: none;
  border-color: rgba(255, 122, 90, 0.55);
  box-shadow:
    0 12px 32px rgba(0, 0, 0, 0.42),
    0 0 24px rgba(255, 122, 90, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.12);
  transform: translateY(-1px);
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide--why-pricing .wpc__card--dcontext:active {
  transform: translateY(0);
}

/* Wider panel + bigger title, scoped so the Buyer modal stays at 520px
 * and its title stays 17px. Panel positioning + viewport clamping is
 * handled by wireHoverPopover() in app.js. */
.bcm--dcm .bcm__panel {
  width: min(620px, calc(100% - 32px));
}
.bcm--dcm .bcm__header {
  padding: 20px 24px 18px;
}
.bcm--dcm .bcm__title {
  font-size: 18px;
  line-height: 1.3;
}

/* Body content: comfortable padding so the copy has room to breathe. */
.bcm--dcm .bcm__body {
  padding: 22px 24px 24px;
}

/* Lede: the one-line takeaway that leadership reads first. Slightly
 * larger + heavier than section body so it stands out at a glance. */
.dcm__lede {
  margin: 0 0 20px;
  font-family: "InspireTWDC", "Open Sans", Inter, sans-serif;
  font-size: 16px;
  line-height: 1.5;
  font-weight: 600;
  color: #111827;
}

.dcm__section {
  margin-top: 20px;
}
.dcm__section:first-of-type {
  margin-top: 0;
}
.dcm__section-label {
  margin: 0 0 8px;
  font-family: "InspireTWDC", "Open Sans", Inter, sans-serif;
  font-size: 14px;
  line-height: 20px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #6B7280;
}
.dcm__section-body {
  margin: 0;
  font-family: "InspireTWDC", "Open Sans", Inter, sans-serif;
  font-size: 15px;
  line-height: 1.55;
  color: #374151;
}
.dcm__section-body + .dcm__section-body {
  margin-top: 6px;                 /* small gap between the two Example lines */
}

/* Impact definition list: bold term + plain-language description on
 * each row, with a small dot bullet so the list is scannable in ~5
 * seconds without feeling like a dense paragraph. */
.dcm__impact {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.dcm__impact-item {
  position: relative;
  padding-left: 16px;
  font-family: "InspireTWDC", "Open Sans", Inter, sans-serif;
  font-size: 15px;
  line-height: 1.55;
  color: #374151;
}
.dcm__impact-item::before {
  content: "";
  position: absolute;
  left: 4px;
  top: 10px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: #9CA3AF;
}
.dcm__impact-term {
  color: #111827;
  font-weight: 700;
}

/* Narrow-viewport tightening: keep the wider Deal Context panel readable
 * on the smallest supported widths without ever exceeding the viewport. */
@media (max-width: 640px) {
  .bcm--dcm .bcm__panel  { width: calc(100% - 32px); }
  .bcm--dcm .bcm__header { padding: 16px 18px 14px; }
  .bcm--dcm .bcm__body   { padding: 18px 18px 20px; }
}

/* ==========================================================================
 * Downstream hover popover (.bcm--dsm)
 * --------------------------------------------------------------------------
 * Sibling of the Deal Context .bcm--dcm modal, wired by the same
 * wireHoverPopover() factory in app.js. The Downstream overlay carries
 * BOTH .bcm--dcm AND .bcm--dsm classes so it inherits every Deal Context
 * panel rule above (620px width, 20px/24px header padding, 18px title,
 * 22px/24px body padding, .dcm__lede / .dcm__section-* / .dcm__impact-*
 * typography, and narrow-viewport tightening) with zero duplication.
 *
 * The .bcm--dsm namespace exists purely so future Downstream-only
 * overrides have a home. Per the "same modal style as Deal Context"
 * brief there are no divergent rules today; keeping the selector
 * reserved makes it easy to layer overrides later without touching
 * the shared .bcm--dcm block.
 *
 * Only the v1.2-scoped Downstream tile hover treatment is unique to
 * this block. It mirrors the Deal Context tile hover (coral border
 * glow, subtle translate-y lift) so the trigger surfaces on the
 * "Why Pricing Gets Complicated Before Planning" slide feel like the
 * same interactive component.
 * ========================================================================== */

/* v1.2-only hoverable Downstream tile - mirror of the Buyer and Deal
 * Context tile hover treatment above so all three trigger surfaces feel
 * identical. Same reason for pointer-events: auto (atlas-stage sets it
 * to none on child cards to let the slide's wrap-click reach them). */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide--why-pricing .wpc__card--downstream {
  pointer-events: auto;
  cursor: default;
  transition: transform 160ms ease, border-color 160ms ease, box-shadow 160ms ease;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide--why-pricing .wpc__card--downstream:hover,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide--why-pricing .wpc__card--downstream:focus-visible {
  outline: none;
  border-color: rgba(255, 122, 90, 0.55);
  box-shadow:
    0 12px 32px rgba(0, 0, 0, 0.42),
    0 0 24px rgba(255, 122, 90, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.12);
  transform: translateY(-1px);
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide--why-pricing .wpc__card--downstream:active {
  transform: translateY(0);
}

/* ==========================================================================
 * Base Rate hover popover (.bcm--brm)
 * --------------------------------------------------------------------------
 * Sibling of the Downstream .bcm--dsm modal, wired by the same
 * wireHoverPopover() factory in app.js. The Base Rate overlay carries
 * BOTH .bcm--dcm AND .bcm--brm classes so it inherits every Deal Context
 * panel rule (620px width, 20px/24px header padding, 18px title,
 * 22px/24px body padding, .dcm__lede / .dcm__section-* / .dcm__impact-*
 * typography, and narrow-viewport tightening) with zero duplication.
 *
 * The .bcm--brm namespace exists purely so future Base-Rate-only
 * overrides have a home. Per the "same modal style as the other hover
 * modals" brief there are no divergent rules today; keeping the
 * selector reserved makes it easy to layer overrides later without
 * touching the shared .bcm--dcm block.
 *
 * Only the v1.2-scoped Base Rate tile hover treatment is unique to
 * this block. It mirrors the Deal Context and Downstream tile hover
 * treatments (coral border glow, subtle translate-y lift) so all four
 * trigger surfaces on the "Why Pricing Gets Complicated Before
 * Planning" slide feel like the same interactive component.
 * ========================================================================== */

/* v1.2-only hoverable Base Rate tile - mirror of the Buyer, Deal
 * Context, and Downstream tile hover treatments above so all four
 * trigger surfaces feel identical. Same reason for pointer-events:
 * auto (atlas-stage sets it to none on child cards to let the slide's
 * wrap-click reach them). */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide--why-pricing .wpc__card--baserate {
  pointer-events: auto;
  cursor: default;
  transition: transform 160ms ease, border-color 160ms ease, box-shadow 160ms ease;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide--why-pricing .wpc__card--baserate:hover,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide--why-pricing .wpc__card--baserate:focus-visible {
  outline: none;
  border-color: rgba(255, 122, 90, 0.55);
  box-shadow:
    0 12px 32px rgba(0, 0, 0, 0.42),
    0 0 24px rgba(255, 122, 90, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.12);
  transform: translateY(-1px);
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide--why-pricing .wpc__card--baserate:active {
  transform: translateY(0);
}

/* ==========================================================================
 * v1.2 explainer footer text removal (PM cleanup, 2026-07-09).
 *
 * Hides the "Disney Ad Platforms Design - Quote-to-Cash (Q2C)" footer that
 * sits in the bottom-left corner of the HTML atlas slides. Each applicable
 * slide uses its own footer class (.wpc__footer). The rules are scoped
 * under body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) so v1.1 and v1.0 continue to show the
 * footer unchanged.
 *
 * NOTE: the small brand-mark icon in the bottom-right corner uses
 * different classes (.wpc__brand / .rcd__brand / .cw__brand)
 * and is deliberately NOT hidden here - the PM asked to keep it.
 *
 * All footer selectors use position: absolute so display: none does not
 * shift any surrounding layout. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .atlas-slide--why-pricing .wpc__footer {
  display: none;
}

/* ==========================================================================
 * ADS Toast v1.2 overrides (2026-07-09 restyle brief)
 * --------------------------------------------------------------------------
 * Source: ADS Toast in Figma file DJb3yM8aOQjIAbTQ9Cd8Mi node 70:62.
 *
 * Why this block exists:
 *   The base .ads-toast rules (~line 2653) match the earlier Figma
 *   node 70:32 which used softer accent hues and a fixed 400px width.
 *   PM's 2026-07-09 restyle brief pointed to the v2.1 Toast (node
 *   70:62) which introduces:
 *     - Exact accent + text tokens per state:
 *         success  border #6CB288 / text #365944
 *         warning  border #F5BB7A / text #5C3711
 *         info     border #4045C2 / text #4045C2 (headline = accent)
 *         error    border #DB5461 / text #993B44
 *     - Elevation/30 drop shadow: 0 4px 6px rgba(15,18,20,0.30)
 *     - Content-sized width (short messages must not stretch across
 *       the top area). Cap at the ADS 400px width for long messages.
 *
 * We scope every override under body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) so v1.0 and
 * v1.1 keep the earlier softer palette / 400px block / 0 4 12 0.15
 * elevation byte-for-byte. Border-radius intentionally stays 12px per
 * Fidel's project-wide radius audit (see the token block near line
 * 6987); the ADS spec is 6px but the local v1.2 standard wins.
 *
 * Content-sized width strategy:
 *   .ads-toast-stack aligns children to flex-end so each toast hugs
 *   the right rail of the stack. Individual .ads-toast entries drop
 *   the hardcoded 400px, use width: auto with min-width 320px, and
 *   cap at 400px. Short messages ("Rate card duplicated.") land near
 *   the min; longer messages ("Complete required fields before
 *   continuing.") grow toward the max. Height is naturally compact
 *   because we already use `align-items: flex-start` on the toast
 *   flex + padding: 16px, so a single-line message renders at ~56px.
 * ========================================================================== */

/* ---- Shared toast placement + elevation tokens, v1.2 only (2026-07-16) *
 *
 * Brief: standardize toast position and shadow across ALL variants
 * (success / info / warning / error) and ALL toast components:
 *   - .ads-toast   fired from app.js toast() for RCM list, Edit, Quick
 *                  Edit, Create, and every other in-app flow.
 *   - .dlsim-toast fired from download-sim.js showSuccessToast() when
 *                  the Download / Upload sim modal closes.
 *
 * Pre-fix state, audited on 2026-07-16:
 *
 *   Component        Position (top / right)    Shadow
 *   ---------------  ------------------------  --------------------------
 *   .ads-toast-stack   top 60px / right 32px    0 12 24 0.20 + 0 4 8 0.10
 *   .dlsim-toast-host  top 24px / right 24px    0 4 6 0.18
 *
 * So a Rate card duplicated toast (via .ads-toast) landed 36px lower
 * and 8px farther in from the right edge than a Blank template
 * downloaded toast (via .dlsim-toast), AND the download toast lived
 * BEHIND the .gnav header (56px tall). The two also carried different
 * elevation strengths, so the same "top-right notification" surface
 * read as two different components.
 *
 * The v1.2 .ads-toast shadow (double cast, 0 12 24 0.20 + 0 4 8 0.10)
 * is the approved reference per the current QA screenshot; the fix
 * applies that exact shadow to every toast in the ADS project.
 *
 * Gap between stack wrapper and toast card (the "yellow arrow" in
 * the QA screenshot):
 *   The previous ads-toast-in keyframe started at
 *     from { opacity: 0; transform: translateY(-6px); }
 *   with no animation-fill-mode. During the 160ms entrance, the toast
 *   paints 6px ABOVE its .ads-toast-stack top: 60px anchor. A probe
 *   (250ms after toast() call) measured
 *     stack top=60px, toast top=54px, gap=-6px
 *   so the visible toast card sits 6px outside its positioning
 *   wrapper. The .dlsim-toast entrance had the same shape (transition
 *   from translateY(-8px) to translateY(0)), producing an equivalent
 *   8px transient offset. Switching both to opacity-only fade keeps
 *   the arrival cue and locks the toast to its stack anchor for the
 *   full lifecycle. animation-fill-mode both is added on the .ads
 *   -toast keyframe so the pre/post-animation state cannot drift.
 *
 * Consolidation:
 *   1. Shared tokens on body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]):
 *        --toast-anchor-top    (60px)   -> pins below the 56px .gnav
 *        --toast-anchor-right  (32px)   -> matches the .surface gutter
 *        --toast-anchor-gap    (12px)   -> vertical separation between
 *                                          stacked toasts
 *        --toast-max-height    (calc)   -> caps a burst of toasts
 *                                          within the viewport
 *        --toast-elevation     (double) -> reference shadow, reused by
 *                                          every variant + component
 *        --toast-z-index       (10001)  -> above every layer the app
 *                                          currently uses (gnav 45,
 *                                          Quick Edit sheet 1300, etc.)
 *   2. .ads-toast-stack + .dlsim-toast-host share ONE positioning
 *      rule using those tokens. Both hosts land at the exact same
 *      viewport-fixed top-right anchor regardless of page, workflow,
 *      or which toast system fired.
 *   3. .ads-toast + .dlsim-toast share ONE shadow rule using
 *      --toast-elevation, so success / info / warning / error all
 *      carry the same elevation strength / blur / spread / direction.
 *   4. Both entrance animations reduced to opacity-only fade so the
 *      toast card never renders above its stack anchor.
 *
 * Preservation:
 *   - Toast content (icon, title, message, actions, close X, per-
 *     variant accent color, per-variant text color, auto-dismiss
 *     duration, trigger callers, aria-live) UNTOUCHED.
 *   - The variant left-accent stripe (.ads-toast::before) UNTOUCHED.
 *   - v1.0 / v1.1 .ads-toast placement and single-cast shadow
 *     UNTOUCHED (block scoped under body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"])).
 *   - .dlsim-toast outside v1.2 keeps its earlier top 24 / right 24
 *     placement and single-cast shadow (also scoped).
 *
 * Follow-up flagged: consider merging .dlsim-toast into the ads-toast
 *   host so download-sim toasts stack cleanly against RCM toasts if
 *   both ever fire in the same tick. Out of scope here (would require
 *   a JS refactor in download-sim.js that changes trigger logic). */

body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) {
  --toast-anchor-top:   60px;
  --toast-anchor-right: 32px;
  --toast-anchor-gap:   12px;
  --toast-max-height:   calc(100vh - 60px - 24px);
  --toast-max-height:   calc(100dvh - 60px - 24px);
  --toast-z-index:      10001;
  --toast-elevation:
    0 12px 24px 0 rgba(15, 18, 20, 0.20),
    0 4px  8px  0 rgba(15, 18, 20, 0.10);
}

body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-toast-stack,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .dlsim-toast-host {
  position: fixed;
  top:      var(--toast-anchor-top);
  right:    var(--toast-anchor-right);
  z-index:  var(--toast-z-index);
  display:  flex;
  flex-direction: column;
  align-items:    flex-end;
  gap:      var(--toast-anchor-gap);
  max-height: var(--toast-max-height);
  overflow-y: auto;
  overflow-x: hidden;
  pointer-events: none;
  scrollbar-width: none;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-toast-stack::-webkit-scrollbar,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .dlsim-toast-host::-webkit-scrollbar {
  width: 0;
  height: 0;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-toast-stack > *,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .dlsim-toast-host > * {
  pointer-events: auto;
}

body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-toast {
  /* Content-sized width (short messages don't stretch). Cap at the
   * ADS reference max so long messages wrap at the ADS panel width. */
  width: auto;
  min-width: 320px;
  max-width: 400px;
  /* Kill the 5px left border from the base rule so the accent doesn't
   * taper into the 12px border-radius. The ::before below repaints
   * the accent as a proper inset rectangle inside the rounded body. */
  border-left: 0;
  position: relative;
  padding: 16px 16px 16px 20px;
  box-shadow: var(--toast-elevation);
  /* Opacity-only entrance so the toast never renders above its stack
   * anchor (see block comment above). fill-mode both keeps the
   * pre/post-animation state locked to opacity: 1 with no transform. */
  animation: ads-toast-fade-in 160ms ease-out both;
}

/* The entrance above is set on .ads-toast itself rather than on an
   is-visible class, so it outranks the generic dismissing rule and
   would keep replaying the fade-in on the way out. Restating the exit
   at this weight is what lets the toast fade away. v2.0 and v2.1 slide
   instead and override this from v2.css, which loads after. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-toast.is-dismissing {
  animation: ads-toast-fade-out 160ms ease-in both;
}

/* Accent stripe as an inset rectangle. Positioned 6px in from the
 * left edge, 10px from top / bottom of the toast, 4px wide with its
 * own 2px radius. Because it lives inside the rounded body, it can't
 * taper into or leak past the toast's outer border-radius. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-toast::before {
  content: "";
  position: absolute;
  left: 6px;
  top: 10px;
  bottom: 10px;
  width: 4px;
  border-radius: 2px;
  background: var(--ads-toast-info-accent);
  pointer-events: none;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-toast[data-variant="success"]::before { background: var(--ads-toast-success-accent); }
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-toast[data-variant="info"]::before    { background: var(--ads-toast-info-accent); }
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-toast[data-variant="warning"]::before { background: var(--ads-toast-warning-accent); }
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-toast[data-variant="error"]::before   { background: var(--ads-toast-error-accent); }

/* dlsim toast in v1.2: adopt the same reference shadow and neutralize
 * the base translate-based entrance so the card stays flush against
 * its .dlsim-toast-host anchor from the moment it appears. Opacity is
 * still animated via the base .dlsim-toast transition + is-visible /
 * is-leaving classes, unchanged; only the transform is zeroed. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .dlsim-toast {
  box-shadow: var(--toast-elevation);
  transform: none;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .dlsim-toast.is-visible,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .dlsim-toast.is-leaving {
  transform: none;
}

@keyframes ads-toast-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* The mirror of the entrance, and the reason a dismissed toast on
   these versions now actually leaves. The entrance here is opacity
   only, deliberately, so that the toast never paints above its stack
   anchor; the exit keeps that constraint and just runs it backwards. */
@keyframes ads-toast-fade-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* --- ADS v2.1 accent + text tokens per state ----------------------
 * The base tokens are declared at :root so the CSS cascade lets a
 * v1.2-scoped override on `body` change every downstream ads-toast
 * consumer at once. Success already matches, so no override for it. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) {
  --ads-toast-warning-accent: #F5BB7A;
  --ads-toast-warning-text:   #5C3711;
  --ads-toast-info-accent:    #4045C2;
  --ads-toast-info-text:      #4045C2;   /* ADS: info headline uses the accent color */
  --ads-toast-error-accent:   #DB5461;
  --ads-toast-error-text:     #993B44;
}

/* Icon color for info: base rule paints the icon with the info accent
 * token, so no override needed here - the token flip above cascades. */

/* Message-only toasts (no explicit title) on v1.2:
 *   Legacy callers pass just a string. The title collapses via
 *   .ads-toast__title:empty. When the title is empty, the message is
 *   the primary line and should carry the variant's accent-tinted
 *   color so a "success" toast still reads as green, "error" as red,
 *   etc., matching the ADS pattern where the headline is the only
 *   color anchor besides the left border. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-toast[data-variant="success"]:has(.ads-toast__title:empty) .ads-toast__message {
  color: var(--ads-toast-success-text);
  font-weight: 700;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-toast[data-variant="warning"]:has(.ads-toast__title:empty) .ads-toast__message {
  color: var(--ads-toast-warning-text);
  font-weight: 700;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-toast[data-variant="error"]:has(.ads-toast__title:empty) .ads-toast__message {
  color: var(--ads-toast-error-text);
  font-weight: 700;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-toast[data-variant="info"]:has(.ads-toast__title:empty) .ads-toast__message {
  color: var(--ads-toast-info-text);
  font-weight: 700;
}

/* =====================================================================
 *  v1.2 Line Items multi-attach workflow
 *  ---------------------------------------------------------------------
 *  Wraps the existing LINE form in a three-stage flow:
 *    Stage 1 (empty)      Search + Add-line-item button. No form.
 *    Stage 2 (one item)   Search + "Add another line item" + one form
 *                         with a "Line item: XXX" header + Remove.
 *    Stage 3 (many)       Same as Stage 2, plus additional forms
 *                         stacked below with subtle dividers, each with
 *                         its own header + Remove.
 *  Figma refs:
 *    Stage 1 - Klc8JpICWSLwU1LeUVRHtT 364-4588
 *    Stage 2 - Klc8JpICWSLwU1LeUVRHtT 364-4993
 *    Stage 3 - Klc8JpICWSLwU1LeUVRHtT 364-5705
 *    ADS Search - DJb3yM8aOQjIAbTQ9Cd8Mi 37-16 (pill, 100px radius,
 *                 20px icon, Open Sans 14px, states default/focus/filled)
 *  All new chrome is HIDDEN by default so v1.0/v1.1 render exactly as
 *  before. v1.2-scoped rules below unlock the wrapper.
 * ================================================================= */

/* --- Default (v1.0 / v1.1): hide all new chrome ---------------------
 * The wrapper is transparent - the form renders as if the wrapper
 * were absent. The intro helper block is a NEW addition and is
 * hidden here so the existing .create-form__sectionhead helper
 * inside the form remains the single source of intro copy on v1.1.
 * Dual-label spans default to v1.1 text; v1.2 span hidden.
 *
 * 2026-07-13: extended to Premium adjustments (.premitems / .premitem)
 * so the two sections share one visual grammar. Everything below
 * that reveals or shapes .lineitems / .lineitem also applies to
 * .premitems / .premitem via the comma-list selectors. */
.lineitems__intro,
.lineitems__controls,
.lineitem__head,
.premitems__intro,
.premitems__controls,
.premitem__head,
[data-label-v12] {
  display: none;
}
[data-label-v11] {
  display: inline;
}
/* Remove any accidental extra margin when the header is hidden. */
.lineitem,
.premitem {
  display: block;
}

/* =====================================================================
 *  v1.2-scoped: unlock the multi-attach chrome
 * ================================================================= */

/* Reveal the intro block on v1.2 and switch to the v1.2 helper copy.
 * The form-internal .create-form__sectionhead is hidden below so intro
 * copy has a single source on v1.2. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitems__intro,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitems__intro { display: block; }
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) [data-help-v11] { display: none; }
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) [data-help-v12] { display: block; }

/* Reveal v1.2 field labels + hide v1.1 field labels. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) [data-label-v11] { display: none; }
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) [data-label-v12] { display: inline; }

/* Reveal v1.2 item headers (title + Remove) for both sections. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitem__head,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitem__head {
  display: flex;
  align-items: center;
  gap: 16px;
  margin: 0 0 16px 0;
}

/* Reveal the controls row (search + Add button) for both sections. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitems__controls,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitems__controls {
  display: flex;
  align-items: center;
  gap: 16px;
  position: relative;   /* anchor for .ads-search__results absolute */
  flex-wrap: nowrap;
  margin: 0 0 24px 0;
}

/* Attached-only visibility. Items default to hidden in v1.2; only
 * items with .is-attached (set by JS updateStage()) render. This keeps
 * detached item-0 out of view when it's been removed but still sits
 * in the DOM (item 0 is the canonical form and can't be actually
 * removed without breaking existing save/validate wiring). */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitem,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitem {
  display: none;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitem.is-attached,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitem.is-attached {
  display: block;
}
/* When 0 items are attached, drop the bottom margin under the controls
 * row so the empty section reads as one calm block, not a form-shaped
 * hole. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitems[data-stage="1"] .lineitems__controls,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitems[data-stage="1"] .premitems__controls {
  margin-bottom: 0;
}

/* Wrapper spacing: 16px between intro copy and controls row, then
 * everything else flows from the controls row's own margin. */
.lineitems,
.premitems { display: block; }
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitems__intro,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitems__intro {
  margin: 0 0 16px 0;
}

/* Hide the per-form sectionhead help in v1.2 - the intro copy above
 * the search bar owns the helper text now. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) [data-lineitem-sectionhead],
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) [data-premitem-sectionhead] {
  display: none;
}

/* =====================================================================
 *  ADS Search component (Figma 37-16)
 *  Pill container: 100px radius, 12/8 padding, 20px icon.
 *  States: default (rest), focus (indigo ring), filled (with clear).
 * ================================================================= */

.theme-ads .ads-search {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--ads-search-gap);
  width: 400px;
  max-width: 100%;
  height: var(--ads-search-height);
  box-sizing: border-box;
  padding:
    var(--ads-search-padding-y)
    var(--ads-search-padding-x);
  background: var(--ads-search-rest-background);
  border: var(--ads-input-border-width) solid var(--ads-search-border-default);
  border-radius: var(--ads-search-radius);
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.theme-ads .ads-search:hover {
  border-color: var(--ads-search-border-bold);
}
.theme-ads .ads-search:focus-within {
  border-color: var(--ads-search-border-bold);
  box-shadow: none;
}
.theme-ads .ads-search[data-filled="true"] {
  border-color: var(--ads-search-border-default);
}
.theme-ads .ads-search[data-filled="true"]:hover,
.theme-ads .ads-search[data-filled="true"]:focus-within {
  border-color: var(--ads-search-border-bold);
}
.theme-ads .ads-search:has(.ads-search__input:disabled) {
  background: var(--ads-search-disabled-background);
  border-color: var(--ads-search-border-disabled);
  opacity: var(--ads-search-disabled-opacity);
  cursor: not-allowed;
}
.theme-ads .ads-search:has(.ads-search__input:disabled) .ads-search__icon,
.theme-ads .ads-search:has(.ads-search__input:disabled) .ads-search__input {
  color: var(--ads-input-text-disabled);
}
.theme-ads .ads-search:has(.ads-search__input:disabled) .ads-search__clear {
  display: none;
}

.theme-ads .ads-search__icon {
  position: static;
  inset: auto;
  transform: none;
  display: inline-grid;
  place-items: center;
  flex: 0 0 var(--ads-search-icon-size);
  width: var(--ads-search-icon-size);
  height: var(--ads-search-icon-size);
  pointer-events: none;
}
.theme-ads .ads-search__icon img {
  display: block;
  width: var(--ads-search-icon-leaf-size);
  height: var(--ads-search-icon-leaf-size);
}

.theme-ads .ads-search__input {
  flex: 1 1 auto;
  min-width: 0;
  width: auto;
  height: 20px;
  border: 0;
  border-radius: 0;
  outline: none;
  background: transparent;
  font-family: 'Open Sans', system-ui, sans-serif;
  font-size: 14px;
  line-height: 20px;
  color: var(--ads-search-filled-text);
  padding: 0;
  box-shadow: none;
}
.theme-ads .ads-search__input:hover,
.theme-ads .ads-search__input:focus,
.theme-ads .ads-search__input:focus-visible {
  box-shadow: none;
}
.theme-ads .ads-search__input::-webkit-search-cancel-button {
  -webkit-appearance: none;
}
.theme-ads .ads-search__input::placeholder {
  color: var(--ads-search-rest-text);
  opacity: 1;
}
.theme-ads .ads-search__input:disabled {
  color: var(--ads-input-text-disabled);
}

.theme-ads .ads-search__clear {
  flex: 0 0 var(--ads-search-clear-size);
  width: var(--ads-search-clear-size);
  height: var(--ads-search-clear-size);
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ads-search-rest-text);
  border-radius: var(--ads-search-radius);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 120ms ease, color 120ms ease;
}
.theme-ads .ads-search__clear img {
  display: block;
  width: var(--ads-search-clear-leaf-size);
  height: var(--ads-search-clear-leaf-size);
}
.theme-ads .ads-search__clear[hidden] { display: none; }
.theme-ads .ads-search__clear:hover {
  background: var(--ads-surface-hover);
  color: var(--ads-text-primary);
}
.theme-ads .ads-search__clear:focus-visible {
  outline: 2px solid var(--ads-brand);
  outline-offset: 2px;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitems__help,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitems__help {
  color: var(--ads-text-tertiary);
  font: 400 var(--ads-label-size)/var(--ads-label-line) "Open Sans", sans-serif;
}

/* =====================================================================
 *  ADS Search results dropdown (floating layer)
 *  Positioned absolute to the parent .lineitems__controls or
 *  .premitems__controls so it clears the search bar visually and can
 *  extend beyond the parent card as needed.
 * ================================================================= */

body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__results {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 40;
  min-width: 480px;
  max-width: 640px;
  max-height: 420px;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid rgba(15, 18, 20, 0.12);
  border-radius: 12px;
  /* ADS Elevation/30 */
  box-shadow:
    0 4px 12px rgba(23, 29, 35, 0.10),
    0 12px 32px rgba(23, 29, 35, 0.12);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__results[hidden] {
  display: none;
}

/* Group header (e.g. "Existing line items") */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__group-head {
  font-family: 'Open Sans', system-ui, sans-serif;
  font-size: 14px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: #51585b;
  padding: 10px 12px 6px 12px;
}

body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__result {
  display: block;
  width: 100%;
  padding: 10px 12px;
  border: 0;
  background: transparent;
  border-radius: 6px;
  text-align: left;
  cursor: pointer;
  color: #161c1e;
  transition: background 90ms ease;
  outline: none;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__result:hover,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__result[aria-selected="true"],
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__result.is-active {
  background: var(--indigo-5);
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__result[data-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__result[data-disabled="true"]:hover {
  background: transparent;
}

body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__result-line1 {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-family: 'Open Sans', system-ui, sans-serif;
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;
  color: #161c1e;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__result-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__result-rate {
  flex: 0 0 auto;
  color: #3b4347;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__result-line2 {
  display: flex;
  gap: 6px 12px;
  flex-wrap: wrap;
  margin-top: 4px;
  font-family: 'Open Sans', system-ui, sans-serif;
  font-size: 14px;
  line-height: 18px;
  color: #51585b;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__result-meta {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__result-meta strong {
  color: #3b4347;
  font-weight: 600;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__result-attached {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--indigo-70);
  font-weight: 600;
  margin-top: 4px;
  font-size: 14px;
  line-height: 18px;
}

body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__empty {
  padding: 20px 16px;
  text-align: center;
  color: #51585b;
  font-family: 'Open Sans', system-ui, sans-serif;
  font-size: 14px;
  line-height: 20px;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__empty strong {
  color: #1e2528;
  font-weight: 600;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__empty-hint {
  margin-top: 6px;
  font-size: 14px;
  line-height: 18px;
  color: #51585b;
}

/* =====================================================================
 *  Item header + Remove control (Figma 364-4993)
 *  Bold "Line item: XXX" / "Premium adjustment: XXX" (Open Sans Bold
 *  14px) and indigo "Remove" action (Open Sans SemiBold 12px). In
 *  Figma the Stage 3 Remove reads as a small outlined pill so it stays
 *  discoverable across multiple stacked forms; we honor that
 *  treatment consistently.
 *
 *  Both Line items and Premium adjustments share the same visual
 *  grammar so the two sections read as members of the same component
 *  family, per the "Visual consistency with Line items" brief.
 * ================================================================= */

body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitem__head,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitem__head {
  padding: 0;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitem__title,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitem__title {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-family: 'Open Sans', system-ui, sans-serif;
  font-size: 14px;
  line-height: 20px;
  color: #3b4347;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitem__title-key,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitem__title-key {
  font-weight: 700;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitem__title-val,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitem__title-val {
  font-weight: 700;
  color: #161c1e;
}

body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitem__remove,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitem__remove {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border: 1px solid rgba(64, 69, 194, 0.28);
  background: transparent;
  color: var(--indigo-70);
  border-radius: 6px;
  font-family: 'Open Sans', system-ui, sans-serif;
  font-size: 14px;
  line-height: 18px;
  font-weight: 600;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitem__remove:hover,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitem__remove:hover {
  background: var(--indigo-5);
  border-color: var(--indigo-70);
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitem__remove:focus-visible,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitem__remove:focus-visible {
  outline: 2px solid var(--indigo-70);
  outline-offset: 2px;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitem__remove svg,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitem__remove svg {
  color: currentColor;
}

/* =====================================================================
 *  Divider between multiple stacked item forms (Stage 3)
 *  Subtle hairline. Applied via :not(:first-child) so the first
 *  item never gets a top divider.
 * ================================================================= */

body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitems__list .lineitem.is-attached + .lineitem.is-attached,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitems__list .premitem.is-attached + .premitem.is-attached {
  margin-top: 24px;
  padding-top: 24px;
  border-top: 1px solid rgba(15, 18, 20, 0.10);
}

/* Flash treatment for duplicate-attach feedback */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitem.is-flash,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitem.is-flash {
  animation: lineitem-flash 900ms ease;
}
@keyframes lineitem-flash {
  0%   { box-shadow: 0 0 0 0 rgba(64, 69, 194, 0.0); }
  35%  { box-shadow: 0 0 0 6px rgba(64, 69, 194, 0.16); }
  100% { box-shadow: 0 0 0 0 rgba(64, 69, 194, 0.0); }
}

/* =====================================================================
 *  Responsive: at narrower desktop widths, allow the search bar to
 *  fill available space and keep the Add button visible. Below the
 *  1180px breakpoint the search shrinks and the results dropdown
 *  aligns to the search bar's right edge if it would overflow.
 * ================================================================= */

@media (max-width: 1180px) {
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search {
    width: 340px;
  }
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__results {
    min-width: 340px;
  }
}
@media (max-width: 960px) {
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitems__controls,
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitems__controls {
    flex-wrap: wrap;
  }
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search {
    width: 100%;
  }
  body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .ads-search__results {
    left: 0;
    right: 0;
    min-width: 0;
    max-width: none;
  }
}

/* Keep the 3-column form grid at desktop widths (existing
 * .create-form__row--three already handles this) but ensure the item
 * card itself has 0 padding so it inherits from the parent accordion
 * body. Applies to both Line items and Premium adjustments. */
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitem,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitem {
  padding: 0;
  margin: 0;
}
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .lineitem > form,
body:is([data-version="1.2"], [data-version="2.0"], [data-version="2.1"]) .premitem > form {
  margin: 0;
}

/* ==========================================================================
 * Production fit-to-viewport contract
 * Keeps application routes fluid while dense data owns its local overflow.
 * Atlas uses its independent logical-canvas fitter in app.js.
 * ======================================================================= */
body:not([data-route="atlas"]) .page-route,
body:not([data-route="atlas"]) .page__top,
body:not([data-route="atlas"]) .page__header,
body:not([data-route="atlas"]) .page__heading,
body:not([data-route="atlas"]) .page__actions,
body:not([data-route="atlas"]) .page__content,
body:not([data-route="atlas"]) .surface,
body:not([data-route="atlas"]) .toolbar,
body:not([data-route="atlas"]) .table-scroll {
  min-width: 0;
  max-width: 100%;
}

body:not([data-route="atlas"]) .page-route {
  width: 100%;
  min-height: calc(100vh - var(--app-header-block-size));
  min-height: calc(100dvh - var(--app-header-block-size));
}

body:not([data-route="atlas"]) .table-scroll,
body:not([data-route="atlas"]) .qsheet__grid-wrap {
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
}

.qsheet {
  padding-top: var(--app-safe-area-top);
}

.qsheet__panel {
  max-height: calc(100vh - var(--app-safe-area-top));
  max-height: calc(100dvh - var(--app-safe-area-top));
}

.qsheet__footer {
  padding-bottom: max(16px, var(--app-safe-area-bottom));
}

.fpanel {
  max-width: calc(
    100% - var(--app-safe-area-left) - var(--app-safe-area-right)
  );
}

@media (max-width: 899px) {
  body:not([data-route="atlas"]) .page__top .page__actions {
    display: flex;
    flex-wrap: wrap;
    width: 100%;
  }

  body:not([data-route="atlas"]) .page__top .page__actions > * {
    max-width: 100%;
  }

  .qsheet__panel {
    height: 84vh;
    height: 84dvh;
    min-height: 0;
  }
}

/* =====================================================================
 *  VERSION 2.1 LIST VIEW
 *
 *  References (sole visual source of truth for this block):
 *    Figma 472:17713   default table state (superseded column set below)
 *    Figma 608:12966   one row selected + contextual action bar
 *    Figma 645:68987   2026-08-15 brief: combines Rate Card ID + Name
 *                      into one "Rate Card ID / Name" column and
 *                      restores Buying Entity to the 2.1 track set
 *
 *  2.1 was forked from 2.0 as a byte-identical duplicate. Every rule
 *  below is a deliberate divergence measured off those nodes and
 *  scoped to body[data-version="2.1"], so 2.0 stays frozen.
 *
 *  Geometry read off the 1440x932 reference frame:
 *    gnav 56h | sidebar 64w | title y=88 (30/36) | subtitle y=132
 *    (14/20) | table card x=101 y=169 w=1314 r=12 | toolbar 60h |
 *    column header row 32h | data rows 56h (2026-08-15: was 48h before
 *    the combined column needed a second line) | pagination band 52h
 *
 *  Column tracks per 645:68987, in visual order:
 *    Selection 28 | Status 96 | Rate Card ID / Name 480 | SalesHub ID
 *    168 | Marketplace 112 | Buying Entity 160 | Last updated 112 |
 *    Version 96
 *
 *  Rate Card ID / Name replaces the two separate Rate Card ID (276) and
 *  Name (416) tracks 472:17713 had, moves ahead of SalesHub ID in
 *  visual order, and lands at 480 / 1224 = 39% of the fixed-width row
 *  content - inside the 35-40% brief target. Buying Entity (dropped by
 *  472:17713, restored by 645:68987) sits between Marketplace and Last
 *  updated, matching 2.0's column, at 160px rather than 2.0's 170px.
 *  Version keeps its 96px HEADER width rather than its 48px data width,
 *  because one grid track serves both and the "Version" label plus sort
 *  glyph does not fit in 48px.
 *
 *  Two deviations kept from the original trace, each argued at its own
 *  rule:
 *    1. Page gutters are a symmetric 29px instead of the reference's
 *       33 left / 25 right. The reference frame puts its own header at
 *       x=95 and its card at x=101, so it has no single left edge to
 *       copy; 29px reproduces the card width (1314) exactly and lands
 *       both edges between the two values the reference uses.
 *    2. The selection column and the action bar are pinned to the
 *       scrollport when the table is narrower than its columns. The
 *       reference frame is exactly wide enough that it never scrolls, so
 *       it says nothing about this case; at the reference width both
 *       pins are inert.
 *
 *  Specificity note: these rules deliberately match, rather than
 *  exceed, the shared rules they override and win by being later in
 *  the file. Keep this block last so that stays true.
 * ===================================================================== */

/* Reference colors with no equivalent in the ADS token block above.
 * Scoped to 2.1 rather than added to the global palette so nothing
 * outside this version can pick them up by accident. */
body[data-version="2.1"] {
  --v21-indigo-700:  #2D2F8C;  /* Name link, I474:18834;4722:44190     */
  --v21-gray-800:    #161C1E;  /* color/gray/800, all body + heading   */
  /* Hover wash for ghost buttons sitting on the indigo action bar. The
   * ADS surface-hover token is a dark 5% wash built for light
   * surfaces; on the brand indigo it is invisible, and ADS carries no
   * inverse-surface equivalent. */
  --v21-inverse-hover: rgba(255, 255, 255, 0.12);
  --v21-page-gutter: 29px;
  /* Table header cell, Q2C ADS Design Style Guide 104:23620-104:23640.
   * The style guide draws header labels in font/body/semibold/sm
   * (Open Sans 600, 12/18, 0 tracking) over color/text/tertiary, with
   * an 8px inline inset inside a 32px white row.
   *
   * color/text/tertiary reads #666D70 in the style guide, while the
   * theme-wide --ads-text-tertiary is still #51585B. Only the header
   * is retuned here; the global token is left alone so the other 28
   * call sites keep their current color. */
  --ads-table-header-text:        #666D70;
  --ads-table-header-font-size: 14px;
  --ads-table-header-line-height: 18px;
  --ads-table-header-font-weight: 600;
  --ads-table-header-padding-x:   8px;
}

/* ---------------------------------------------------------------------
 *  1. Page canvas
 *
 *  The reference has no white slab behind the title, subtitle, and
 *  header buttons: they sit directly on the page canvas with only the
 *  table card floating on it.
 * ------------------------------------------------------------------ */
body[data-version="2.1"] .page__top {
  background: var(--page-canvas-background);
}

/* No rule below the header block. The shared ADS rule gives .page__top a
 * 1px recessed border because in 2.0 it divides a white header band from
 * the gray band beneath it. 2.1 paints both bands the same canvas color,
 * so that border stops separating anything and instead draws a seam
 * across one continuous surface, which the reference does not have.
 * Scoped to the list view: the 2.1 Create page still has a white band
 * over a gray one, so there the border is still doing its job. */
body[data-version="2.1"] [data-page="list"] > .page__top {
  border-bottom: 0;
}

/* Vertical rhythm: title top at y=88 is 32px below the 56px gnav, and
 * the card top at y=169 is 17px below the 152px bottom of the header
 * block. 2.0 spends 65px there, which reads as a much airier page. */
body[data-version="2.1"] [data-page="list"] > .page__top {
  padding-top: 32px;
  padding-bottom: 17px;
  padding-left: var(--v21-page-gutter);
  padding-right: var(--v21-page-gutter);
}

/* The gap above the card. It used to come from the white .page__top
 * band; the header lives inside the card now, so the gray canvas owns
 * it. Figma 472:17713 puts the card top at y=86, 30px below the 56px
 * global header. */
body[data-version="2.1"] [data-page="list"] > .page__content {
  padding-top: 30px;
  padding-left: var(--v21-page-gutter);
  padding-right: var(--v21-page-gutter);
}

/* Header block and card share one cap so the title, the buttons, and
 * the card keep a single left and right edge. 1314 is the reference
 * card width; at 1440 the gutters already resolve to exactly that, so
 * the cap only binds above 1440. The 1600+ step-ups further up the
 * file still apply, so wide screens do not strand the card. */
body[data-version="2.1"] .page__top > .page__header,
body[data-version="2.1"] .page__content > .surface {
  max-width: 1314px;
}

body[data-version="2.1"] .page__title,
body[data-version="2.1"] .page__subtitle {
  color: var(--v21-gray-800);
}

/* ---------------------------------------------------------------------
 *  2. Table card
 *
 *  1px black/10 hairline, 8px radius (474:18799 rounds its top corners
 *  to 8), no elevation. 2.0 carries a 16px radius and a drop shadow.
 * ------------------------------------------------------------------ */
body[data-version="2.1"] .page__content > .surface {
  border: 1px solid var(--ads-border-recessed);
  border-radius: var(--ads-radius-md);
  box-shadow: none;
}

/* ---------------------------------------------------------------------
 *  3. Toolbar
 *
 *  The reference filter control is a 32x32 icon-only button (474:18814)
 *  with the funnel glyph and no label, 16px from the card edge, and the
 *  search field starts 16px after it at 399px wide.
 * ------------------------------------------------------------------ */
body[data-version="2.1"] .filter-btn {
  width: 32px;
  /* The shared rule carries min-width: 72px for the label it no longer
   * has to fit, and min-width would otherwise beat width. */
  min-width: 32px;
  height: 32px;
  padding: 0;
  justify-content: center;
}

body[data-version="2.1"] .toolbar .search {
  width: 399px;
}

/* The reference row is items-start, so the 32px filter button sits on
 * the same 16px top edge as the taller 36px search field rather than
 * centering against it. */
body[data-version="2.1"] .toolbar {
  align-items: flex-start;
}

/* The shared search maps its placeholder to the ADS tertiary text token,
 * which is dark enough that the hint reads as a typed value. The
 * reference draws it in a much lighter gray, so 2.1 uses the project's
 * own placeholder token, the same one every other field family here
 * uses. Kept as a token rather than the reference's literal EDL Gray/40
 * hex, per the v1 to v2.1 migration rule. */
body[data-version="2.1"] .search__input::placeholder {
  color: var(--placeholder-color);
}

/* Label is dropped visually only. aria-label="Filter rate cards" on
 * the button still names the control for assistive tech. */
body[data-version="2.1"] .filter-btn > span {
  display: none;
}

/* ---------------------------------------------------------------------
 *  4. Column tracks
 *
 *  Overriding the --col-* custom properties rather than writing literal
 *  widths into grid-template-columns keeps the column resizer working:
 *  it writes the same properties inline on .table, which still beats
 *  this rule.
 * ------------------------------------------------------------------ */
body[data-version="2.1"] .table {
  /* One ADS Checkbox is 28px wide (the 20px control plus the
   * component's own 4px on each side) and the control keeps that
   * position, flush with the row's inline-start padding. The track is
   * wider than the control so the 12px it does not use becomes the
   * gutter between the checkbox and the Status pill, rather than that
   * separation having to come out of Status's own padding. */
  --col-select:    40px;
  --col-status:    96px;
  /* A SalesHub ID is 18 characters of 14px body text, so this is the
   * widest of the fixed tracks: it has to hold the value on one line
   * and still leave the eye a gap before Marketplace. */
  --col-saleshub: 176px;
  /* 2026-08-26: flexible, with its old fixed width as the floor. See the
     note on --col-buyer for why three tracks share the surplus. */
  --col-market:   0.85fr;
  --col-market-min: 120px;
  /* 2026-08-15: Buying Entity is back (645:68987 restores it between
   * Marketplace and Last updated).
   *
   * 2026-08-26: Marketplace, Buying Entity and Last updated are the
   * flexible tracks, and between them they take whatever the fixed
   * columns and the Rate Card ID / Name cap leave over. The row needs at
   * least one such track or it stops short of the card's right edge, and
   * these are the right three: each holds a real word rather than a
   * fixed-format code, and spreading the surplus over all of them keeps
   * any single column from carrying a visible hole. Buying Entity takes
   * the largest share because its values run longest, Last updated next,
   * Marketplace least. At the reference width that is roughly 180 / 163
   * / 154, against a row whose other columns sit close to their content.
   *
   * Weights, not widths, so the three stay in proportion to each other
   * at every viewport instead of one of them absorbing the difference. */
  --col-buyer:    1fr;
  /* Buying Entity's floor is what decides where the table starts to
   * scroll, since it is the last track to give ground: 104 is the
   * width that still lets every column fit a 1280 laptop, where the
   * card has 1137px to work with. Entity names truncate there, which
   * is the intended trade. */
  --col-buyer-min: 104px;
  --col-updated:  0.9fr;
  --col-updated-min: 128px;
  --col-ver:       72px;
  /* Rate Card ID / Name replaces the separate 276px Rate Card ID and
   * 416px Name tracks 472:17713 had, and it stays comfortably the widest
   * column.
   *
   * 2026-08-26: it used to be the flexible track, which meant it also
   * absorbed every pixel the fixed columns did not want: about 500px at
   * the 1440 reference width against a longest name of 331, so roughly a
   * third of the column was empty and SalesHub ID started far to the
   * right of the content it followed. Capping it here leaves the longest
   * name in the book ("Microsoft, Addressable TV Multi-Year 2025-2026")
   * whole with a gutter in line with the rest of the row, and hands the
   * surplus to the three flexible tracks above. Below the reference
   * width this is still the first track to give ground, down to a floor
   * that fits a full rate card name on one line.
   *
   * 2026-08-26: down from 440. The longest name in the book measures
   * 331, so 400 clears it with a gutter the size of the ones either side
   * of Marketplace, and it is the width at which SalesHub ID stops
   * looking marooned from the content it follows. */
  --col-name-id: 400px;
  --col-name-id-min: 380px;

  /* The width the table needs before it has to scroll: every track at
   * its floor, plus the row's 8px padding on each side. Both the rows
   * and .table itself are held to it, so it is stated once here. */
  --table-min-width: calc(
    var(--col-select) + var(--col-status) + var(--col-name-id-min) +
    var(--col-saleshub) + var(--col-market-min) + var(--col-buyer-min) +
    var(--col-updated-min) + var(--col-ver) + 16px
  );
}

/* Visual track order per 645:68987: the selection checkbox leads,
 * Rate Card ID / Name sits in track 3 (ahead of SalesHub ID, which
 * 472:17713 had in track 3 with no combined column to place),
 * Buying Entity returns between Marketplace and Last updated, and
 * there is still no Action track. */
body[data-version="2.1"] .table__head,
body[data-version="2.1"] .row {
  grid-template-columns:
    var(--col-select)
    var(--col-status)
    minmax(var(--col-name-id-min), var(--col-name-id))
    var(--col-saleshub)
    /* Marketplace, Buying Entity and Last updated share the row's spare
     * width in proportion, and each floors at the fixed width it used to
     * be. They yield before Rate Card ID / Name does, in the order their
     * values tolerate it: an entity name truncates more gracefully than
     * an id or a date, both of which have to stay on one line and
     * whole. */
    minmax(var(--col-market-min), var(--col-market))
    minmax(var(--col-buyer-min), var(--col-buyer))
    minmax(var(--col-updated-min), var(--col-updated))
    var(--col-ver);
  /* Reference rows are padded 8px on the inline start; the same on the
   * end keeps Version off the card's edge now that the flexible column
   * pulls the row out to the full width of the card. */
  padding-left: 8px;
  padding-right: 8px;
  min-width: var(--table-min-width);
}

/* Selection column, header and body. The ADS Checkbox brings its own
 * 4px padding, so the cell adds none, and it fills the row's height so
 * the press target is the whole cell rather than the 20px control
 * inside it.
 *
 * Qualified by .table__head / .row so these beat the generic 2.1 .th and
 * .cell padding further down the file, which is 8px on both sides and
 * would push the box out of its own track. */
body[data-version="2.1"] .table__head .th--select,
body[data-version="2.1"] .row .cell--select {
  display: flex;
  align-items: center;
  /* Full row height, so the press target is 40 x 47 rather than the
   * 28px square the control alone would occupy. A 48px row cannot give
   * a 44px-wide target in this track, but it can give the whole
   * height, and that is the part users miss. */
  align-self: stretch;
  padding: 0;
  overflow: visible;
}

body[data-version="2.1"] .th--select .ads-checkbox,
body[data-version="2.1"] .cell--select .ads-checkbox {
  align-items: center;
  height: 100%;
  /* The control keeps its position at the start of the track; the label
   * stretches across the rest of it so the gutter between the checkbox
   * and Status selects the row too, instead of being a dead strip that
   * falls through to the row and opens the rate card. */
  width: 100%;
}

/* The selection column is pinned to the scrollport's left edge.
 *
 * The reference frame is 1440 wide and the table fits it exactly, so
 * nothing in the design says what should happen when it does not: below
 * about 1400 the columns are wider than the card and the table scrolls
 * inside .table-scroll. Left unpinned, scrolling right takes the
 * checkboxes off screen and the only way to select or clear a row is to
 * scroll back, which is the same problem the action bar already solves by
 * sticking, and the same fix 1.2 uses for its leading columns. Pinning
 * costs nothing at the reference width, where there is no scroll to
 * respond to.
 *
 * A pinned cell has to be opaque or the columns passing beneath it show
 * through, so it paints the row's own surface rather than inheriting a
 * transparent one, and composites the hover and selected washes over
 * that surface the way the 1.2 cluster does. */
body[data-version="2.1"] .table__head .th--select,
body[data-version="2.1"] .row .cell--select {
  position: sticky;
  left: 0;
  background: var(--surface);
}

body[data-version="2.1"] .table__head .th--select { z-index: 2; }
body[data-version="2.1"] .row .cell--select      { z-index: 1; }

body.theme-ads[data-version="2.1"] .row:hover .cell--select,
body.theme-ads[data-version="2.1"] .row.is-selected .cell--select {
  background:
    linear-gradient(var(--ads-surface-hover), var(--ads-surface-hover)),
    var(--surface);
}

body[data-version="2.1"] .th--select,
body[data-version="2.1"] .cell--select        { order: 0; }
body[data-version="2.1"] .th--status,
body[data-version="2.1"] .cell--status        { order: 1; }
/* Rate Card ID / Name (2026-08-15, 645:68987) leads Status's neighbor
 * track now, ahead of SalesHub ID - see th--name-id below. The old
 * th--rate-card-id / .name order assignments are gone along with the
 * cells themselves (hidden just below), not just reordered, so a stray
 * rule can never put them back on screen at some accidental order. */
/* Un-hide the header button the base guard above (".th--name-id
 * { display: none; }") hides for every other version. Qualified with
 * .table__head so its specificity beats that unqualified base rule
 * regardless of source order. */
body[data-version="2.1"] .table__head .th--name-id {
  display: inline-flex;
}
body[data-version="2.1"] .th--name-id,
body[data-version="2.1"] .cell--name-id       { order: 2; }
body[data-version="2.1"] .th--saleshub,
body[data-version="2.1"] .cell--saleshub      { order: 3; }
body[data-version="2.1"] .th--marketplace,
body[data-version="2.1"] .cell--marketplace   { order: 4; }
/* Buying Entity (2026-08-15, 645:68987): restored to 2.1, between
 * Marketplace and Last updated, matching 2.0's column order. */
body[data-version="2.1"] .th--buying-entity,
body[data-version="2.1"] .cell--buying-entity { order: 5; }
body[data-version="2.1"] .th--last-updated,
body[data-version="2.1"] .cell--last-updated  { order: 6; }
body[data-version="2.1"] .th--ver,
body[data-version="2.1"] .cell--ver           { order: 7; }

/* A resize handle straddles its column boundary, so it hangs 4px past
 * the right edge of the header cell it belongs to. On every other
 * column that lands inside the row; on the LAST column it overflows the
 * row, and 4px of horizontal overflow is enough to raise a horizontal
 * scrollbar, which shortens the client height, which raises a vertical
 * scrollbar, which shortens the client width, which widens the
 * horizontal overflow. 2.0 never hits this because its last column is
 * Action, which is not sortable and so has no handle. Version is the
 * last column in 2.1 and there is nothing to its right to resize
 * against, so it does not need one. */
body[data-version="2.1"] .th--ver .th-resizer {
  display: none;
}

/* Rate Card ID and Name: superseded by the combined th--name-id /
 * cell--name-id column above (2026-08-15, 645:68987). Hiding the
 * static header cells is enough for the header; buildRow() skips
 * building the two separate body cells under 2.1 entirely (it builds
 * one cell--name-id instead), so no hidden node is left in the row.
 * Both columns are still searchable, sortable, and rendered separately
 * in every earlier version, and the underlying fields are untouched.
 *
 * Action: 2.1 moves every row action into the selected-row action bar
 * (see "7. Contextual action bar" below) instead of a per-row column. */
body[data-version="2.1"] .th--rate-card-id,
body[data-version="2.1"] .th--name,
body[data-version="2.1"] .th--action {
  display: none;
}

/* Sticky-left pinning is a 1.2 feature, not something either reference
 * node shows, and the 2.1 track order breaks the cumulative offsets it
 * relies on (Rate Card ID / Name now sits between Status and SalesHub
 * ID). The 2.1 table is also 1314px wide against 2.0's 1484, so it fits
 * the card outright at the reference width and only scrolls well below
 * it, where pinning 540px of a 900px viewport would hurt more than
 * help. */
body[data-version="2.1"] .row .cell--status,
body[data-version="2.1"] .row .cell--name-id,
body[data-version="2.1"] .table__head .th--status,
body[data-version="2.1"] .table__head .th--name-id {
  /* relative + left:auto, matching the max-width:699px unpin above.
   * These header cells each host an absolutely positioned resize
   * handle, and the handle has to anchor to its own cell: made static,
   * all three would fall through to .table and stack up 4px past the
   * right edge of the row, which is enough overflow to raise a
   * horizontal scrollbar and then a vertical one after it. */
  position: relative;
  left: auto;
  z-index: 0;
}

/* The app-shell reserves a permanent 15px vertical scrollbar gutter on
 * every .table-scroll. The scrollport is sized to its content, so that
 * gutter never carries a scrollbar here, and it holds the header row
 * and every data row 15px short of the card's inner edge, which leaves
 * the row dividers stopping short of the card's right edge. The
 * reference frame runs them the full width, so 2.1's list drops the
 * gutter. Scoped to the list page: the Quick Edit grid still wants it. */
body[data-version="2.1"] [data-page="list"] .table-scroll {
  scrollbar-gutter: auto;
}

/* ---------------------------------------------------------------------
 *  5. Column header row  (474:18817)
 *
 *  32px tall, white, 1px Gray/20 underline, labels 12/18 semibold in
 *  text/tertiary with a 4px gap to the 12px sort glyph.
 * ------------------------------------------------------------------ */
body[data-version="2.1"] .table__head {
  height: 32px;
  min-height: 32px;
  background: var(--ads-surface);
  border-bottom: 1px solid var(--gray-20);
}

body[data-version="2.1"] .th,
body[data-version="2.1"] .th--sortable {
  gap: 4px;
  padding: 0 var(--ads-table-header-padding-x);
  font-size: var(--ads-table-header-font-size);
  line-height: var(--ads-table-header-line-height);
  font-weight: var(--ads-table-header-font-weight);
  color: var(--ads-table-header-text);
}

/* The reference draws the sort affordance in Gray/30, noticeably lighter
 * than the #9CA0A8 the shared rule uses, so the glyph reads as a quiet
 * hint next to the label instead of competing with it. The active
 * direction still takes the brand color from the shared rule.
 *
 * The style guide header row (104:23620-104:23640) is labels only, so
 * the glyph stays out of the rest state and is revealed three ways:
 * the column under the pointer, the column holding keyboard focus, and
 * whichever column is currently sorted (its direction has to stay
 * readable once the pointer leaves). Fading rather than unmounting
 * keeps the 12px box and the row gap reserved, so revealing the glyph
 * never reflows the label or retracks the column. */
body[data-version="2.1"] .th__sort {
  color: var(--gray-30);
  opacity: 0;
}

body[data-version="2.1"] .th--sortable:hover .th__sort,
body[data-version="2.1"] .th--sortable:focus-visible .th__sort,
body[data-version="2.1"] .create-md__table th:hover .th__sort,
body[data-version="2.1"] .create-md__table th button:focus-visible .th__sort,
body[data-version="2.1"] .th__sort[data-sort-state="asc"],
body[data-version="2.1"] .th__sort[data-sort-state="desc"],
body[data-version="2.1"] .th__sort[data-sort-state="ascending"],
body[data-version="2.1"] .th__sort[data-sort-state="descending"] {
  opacity: 1;
}

/* Version reads left-aligned in the reference, like every other
 * column. 2.0 centers it because its track is only 80px wide. */
body[data-version="2.1"] .th--sortable.th--ver {
  text-align: left;
  justify-content: flex-start;
}

/* Header label swap. Only 2.1 shows the reference spellings, so the
 * legacy label is the one that paints everywhere else. */
.th__label--v21 { display: none; }
body[data-version="2.1"] .th__label--legacy { display: none; }
body[data-version="2.1"] .th__label--v21 { display: inline; }

/* ---------------------------------------------------------------------
 *  6. Data rows  (474:18826, row height superseded by 645:68987)
 *
 *  56px tall (2026-08-15: was 48px before the combined Rate Card ID /
 *  Name column needed a second line), 1px Gray/20 divider, cells 8px
 *  inline padding, values 14/20 regular in gray/800.
 * ------------------------------------------------------------------ */
body[data-version="2.1"] .row {
  height: 56px;
  min-height: 56px;
  border-bottom: 1px solid var(--gray-20);
}

body[data-version="2.1"] .cell {
  padding-inline: 8px;
  font-size: 14px;
  line-height: 20px;
  color: var(--v21-gray-800);
}

/* SalesHub ID is plain 14/20 body text in the reference, not the
 * 13/19 the ADS theme gives .cell--code. */
body[data-version="2.1"] .cell--code {
  font-size: 14px;
  line-height: 20px;
  color: var(--v21-gray-800);
}

body[data-version="2.1"] .cell--ver {
  justify-content: flex-start;
  padding: 0 8px;
  color: var(--v21-gray-800);
}

/* Name link: SemiBold 14/20 in indigo/700. 2.0 uses regular weight in
 * indigo/300. Reused as-is inside the combined Rate Card ID / Name
 * cell below - the link keeps exactly this weight/size/color as the
 * dominant first line, no separate rule needed. */
body[data-version="2.1"] .name__link {
  font-weight: 600;
  font-size: 14px;
  line-height: 20px;
  color: var(--v21-indigo-700);
}

body[data-version="2.1"] .name__link:hover {
  color: var(--v21-indigo-700);
  text-decoration: underline;
}

/* A cell value that has to survive its column narrowing: the element
 * gives text-overflow a block box to put its ellipsis in, which a bare
 * text node inside a flex cell has nowhere to do, and min-width:0 lets
 * it shrink rather than hold the track open. */
.cell__text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------------------------------------------------------------------
 *  6b. Combined Rate Card ID / Name cell  (645:68987, 2026-08-15)
 *
 *  Two stacked lines instead of the single-line .cell layout: the Name
 *  link (dominant, reuses .name__link above unmodified) on top, and a
 *  smaller "ID: {rateCardId}" line in the existing secondary gray text
 *  token underneath. Both lines truncate independently with ellipsis
 *  and their own data-tooltip-truncate="auto" trigger (see buildRow()
 *  in app.js), so a long name never pushes a long ID off the row and
 *  vice versa.
 *
 *  The cell overrides the generic 2.1 .cell rule's single-line
 *  white-space:nowrap + flex row layout with a centered flex column,
 *  so the pair reads as one visually balanced unit inside the taller
 *  56px row rather than sitting at the top or bottom of it. */
body[data-version="2.1"] .cell--name-id {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 2px;
  padding-inline: 8px;
  white-space: normal;
  overflow: hidden;
}

body[data-version="2.1"] .cell--name-id .name__link {
  max-width: 100%;
}

/* "ID: {rateCardId}" - prefixed, smaller, secondary-gray, regular
 * weight, one line. Never a link: plain text with its own truncation
 * tooltip so a long id still surfaces its full value on hover. */
body[data-version="2.1"] .name-id__id {
  display: block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 14px;
  line-height: 20px;
  font-weight: 400;
  color: var(--ads-text-secondary);
}

/* Selected row wash (608:12966, the one tinted row while the bar reads
 * "1 item selected"). #F3F3F3 is what black/opacity/5 resolves to over
 * white, so the existing ADS token is the exact reference value. 2.0
 * uses the indigo brand wash here instead. */
body[data-version="2.1"] .row.is-selected {
  background: var(--ads-surface-hover);
}

/* The 1.2 sticky cluster paints its own background on these cells so
 * scrolled content cannot show through. 2.1 has no sticky cells (the
 * position:relative override above turns that off for every column,
 * including the combined Rate Card ID / Name cell), so they must fall
 * back to the row's own background or a selected row would keep white
 * patches. */
body.theme-ads[data-version="2.1"] .row:hover .cell--status,
body.theme-ads[data-version="2.1"] .row:hover .cell--name-id,
body.theme-ads[data-version="2.1"] .row.is-selected .cell--status,
body.theme-ads[data-version="2.1"] .row.is-selected .cell--name-id {
  background: transparent;
}

/* ---------------------------------------------------------------------
 *  7. Contextual action bar  (608:14479)
 *
 *  A full-width band between the column header row and the first data
 *  row. Indigo/800 ground, 1px black/10 underline so it reads as part
 *  of the table rather than floating over it. The count sits in a
 *  standard 12px table cell; the actions are ADS ghost buttons
 *  (8/12 padding, 6px radius, 4px icon gap, 16px icons) in a 4px row.
 *  Height therefore resolves to 44px: 12 + 20 + 12.
 * ------------------------------------------------------------------ */
/* The bar is measured against the width the table is actually showing,
 * not the width its columns add up to. Below roughly 1440 the columns
 * are wider than the card, so the table scrolls inside .table-scroll,
 * and a bar sized to the columns pushes its own actions past the right
 * edge of the card: at 1024 only three of the five could be reached
 * without scrolling the table first, at 768 only one, and at 375 none.
 * Making .table-scroll a query container gives the bar the scrollport
 * as both its width (100cqi) and the width its layout responds to.
 * Scoped to 2.1's list page, the only place the bar exists. */
body[data-version="2.1"] [data-page="list"] .table-scroll {
  container-type: inline-size;
  container-name: table-port;
}

/* A sticky box cannot leave its containing block, and .table is width:100%
 * of the scrollport while its header and rows carry the wider min-width
 * and overflow it. So the bar had nothing to travel across: it scrolled
 * away with the columns even though it asked to stick. Giving .table the
 * same minimum its rows have makes the containing block as wide as the
 * scroll content, which is exactly the travel the pin needs. */
body[data-version="2.1"] [data-page="list"] .table {
  min-width: var(--table-min-width);
}

/* The empty state is the one child of .table that has no columns to line
 * up with, and it centers its message. Left at the table's new full
 * width it would center off screen on a narrow viewport, so it takes the
 * same scrollport treatment the bar has. */
body[data-version="2.1"] [data-page="list"] .table__empty {
  position: sticky;
  left: 0;
  width: 100cqi;
}

body[data-version="2.1"] .rcsel {
  --rcsel-icon-size: 16px;

  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 44px;
  padding-left: 16px;
  padding-right: 16px;
  /* Exactly as wide as the visible table, and pinned there while the
   * columns scroll underneath. When the table fits, the scrollport is
   * the table's own width and this is the reference layout unchanged:
   * the band spans the row above and below it, edge to edge. When it
   * does not fit, the band still spans everything the card is showing,
   * so the count and the actions stay on screen at any scroll offset
   * instead of riding off with the columns. */
  position: sticky;
  left: 0;
  width: 100cqi;
  /* Style guide 104:23927 fills the band with color/indigo/300, the
   * same brand indigo the top bar uses. It replaces the darker
   * indigo/800 this bar carried before, which no longer appears in the
   * ADS table specs. */
  background: var(--ads-brand);
  border-bottom: 1px solid var(--ads-border-recessed);
  color: var(--ads-text-on-primary);
  font-family: "Open Sans", sans-serif;
}

/* display:flex would otherwise defeat the hidden attribute. */
body[data-version="2.1"] .rcsel[hidden] {
  display: none;
}

body[data-version="2.1"] .rcsel__count {
  padding: 12px;
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;
  white-space: nowrap;
}

body[data-version="2.1"] .rcsel__actions {
  display: flex;
  align-items: center;
  gap: 4px;
}

/* Keyboard scrolling of the action strip needs the region focusable,
 * and a focusable region needs a visible focus state. */
body[data-version="2.1"] .rcsel__actions:focus-visible {
  outline: var(--ads-input-border-active) solid var(--ads-text-on-primary);
  outline-offset: 2px;
}

body[data-version="2.1"] .rcsel__btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  /* An action never squashes to fit. Where there is not enough room the
   * bar changes shape (7b below); the button keeps its own size so its
   * label can never be clipped mid word and its target can never fall
   * under the icon it holds. */
  flex: 0 0 auto;
  padding: 8px 12px;
  background: transparent;
  border: 0;
  border-radius: var(--ads-radius-sm);
  color: inherit;
  font: inherit;
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
}

body[data-version="2.1"] .rcsel__btn:hover:not(:disabled) {
  background: var(--v21-inverse-hover);
}

body[data-version="2.1"] .rcsel__btn:active:not(:disabled) {
  background: var(--ads-surface-active);
}

body[data-version="2.1"] .rcsel__btn:focus-visible {
  outline: var(--ads-input-border-active) solid var(--ads-text-on-primary);
  outline-offset: 2px;
}

/* Quick Edit opens one rate card at a time, so it is disabled while
 * more than one row is selected rather than silently acting on the
 * first of them. */
body[data-version="2.1"] .rcsel__btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

body[data-version="2.1"] .rcsel__btn-icon {
  display: inline-flex;
  flex: 0 0 var(--rcsel-icon-size);
}

body[data-version="2.1"] .rcsel__btn-icon svg {
  display: block;
  width: var(--rcsel-icon-size);
  height: var(--rcsel-icon-size);
}

/* ---------------------------------------------------------------------
 *  7b. Contextual action bar, narrow tables
 *
 *  Measured against the widest state the bar has to hold, a four figure
 *  count next to five labelled actions:
 *
 *    count sentence       156px
 *    gap                   16px
 *    five labelled actions 571px
 *    bar padding           32px
 *    ------------------------------
 *    one line needs       775px of scrollport
 *    actions alone need   603px
 *    five compact actions 236px  (5 x 44 target + 4 x 4 gap)
 *
 *  The scrollport is roughly the viewport less the 68px rail and the
 *  page gutters, so 1024 and up clears the one line budget and keeps
 *  the reference layout. Below that the bar earns its space in two
 *  steps rather than shrinking type: first the actions drop to their
 *  own line, then they drop their labels. The count is never what
 *  gives way, since it is the reason the bar is on screen.
 * ------------------------------------------------------------------ */

/* Step 1: the actions take a second line. Both lines still read left
 * to right from the same 16px edge as the table's first column. */
@container table-port (max-width: 774px) {
  body[data-version="2.1"] .rcsel {
    flex-wrap: wrap;
    /* 8px between the two lines, and the same 12px below the actions
     * that the count already has above it, so the band keeps the
     * table's own vertical rhythm rather than looking bottom heavy. */
    gap: 8px 16px;
    padding-bottom: 12px;
  }

  body[data-version="2.1"] .rcsel__count {
    padding-bottom: 0;
  }

  body[data-version="2.1"] .rcsel__actions {
    flex-basis: 100%;
  }
}

/* Step 2: labels give way to the icon each action already carries, and
 * the strip scrolls if even the compact set is too wide (it is under
 * about 270px of scrollport, which is where 320 and 375 land). Losing
 * an action is never on the table, so the fifth one scrolls into view
 * rather than being dropped into a menu or hidden. The count sits on
 * its own line above and does not move. */
@container table-port (max-width: 602px) {
  body[data-version="2.1"] .rcsel__btn-label {
    /* Not display:none: a screen reader that reads a button's contents
     * rather than its aria-label still finds the word. */
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  body[data-version="2.1"] .rcsel__btn {
    /* 44 x 44 touch target in place of the label width the button just
     * gave up, so the compact form is easier to hit, not harder. */
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    padding: 8px;
  }

  body[data-version="2.1"] .rcsel__actions {
    overflow-x: auto;
    /* Room for the focus ring on the first and last button, which the
     * scrollport edge would otherwise clip. */
    padding: 4px;
    margin: -4px;
    scrollbar-width: thin;
  }
}

/* The trailing edge fades while there is more strip to the right, so
 * the scroll is visible on a platform that hides its scrollbars until
 * they are used. app.js sets both classes, and clears the fade once
 * the last action is in view. */
body[data-version="2.1"] .rcsel__actions.is-scrollable:not(.is-scroll-end) {
  mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent);
}

@media (prefers-reduced-motion: no-preference) {
  body[data-version="2.1"] .rcsel__actions {
    scroll-behavior: smooth;
  }
}

/* ---------------------------------------------------------------------
 *  7c. Action Bar specimen  (Redline Mode)
 *
 *  Redline inspects the real bar in the real table rather than a copy
 *  of it, so the specimen is the list page with everything that is not
 *  the table turned off: the page heading and its buttons, the filter
 *  and search toolbar, the pagination footer. What is left is the
 *  column header row, the bar, and enough rows to read the boundary
 *  the bar sits on. Nothing here restyles the bar itself.
 * ------------------------------------------------------------------ */
body[data-specimen="action-bar"] [data-page="list"] .surface__inner > .page__header,
body[data-specimen="action-bar"] [data-page="list"] .toolbar,
body[data-specimen="action-bar"] [data-page="list"] .footer {
  display: none;
}

/* Six rows: enough to show the first data row against the bar and to
 * paint every selection state that maps onto real rows. */
body[data-specimen="action-bar"] [data-page="list"] [data-rows] .row:nth-child(n+7) {
  display: none;
}

/* =====================================================================
   ATLAS SLIDE - Closing / Thank you (Figma node 779:25788)
   ---------------------------------------------------------------------
   Every number below is the Figma frame's own pixel value against the
   1920x1080 stage, so the whole card scales with --atlas-scale like the
   rest of the deck and nothing has to be re-derived per viewport.

   This slide is deliberately static. It is the page a presenter leaves
   on screen while answering questions, so the links have to be readable
   and clickable the instant it appears rather than after a reveal.
   ===================================================================== */
.atlas-slide--closing .clo {
  position: absolute;
  inset: 0;
  color: #FFFFFF;
  /* The deck disables pointer events across the canvas so a click
   * anywhere advances; the links below opt back in individually. */
  pointer-events: none;
}

.atlas-slide--closing .clo__title,
.atlas-slide--closing .clo__appendix {
  position: absolute;
  left: 63px;
  margin: 0;
  width: 1727px;
  font-family: var(--font-family-inspire);
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
  font-size: 56px;
  line-height: 1;
  color: #FFFFFF;
}

.atlas-slide--closing .clo__title    { left: 67px; top: 178px; }
.atlas-slide--closing .clo__appendix { top: 642px; }

/* ---- Explore the prototype ---------------------------------------- */
/* Figma hangs a 93px pill off the centre of a 59px hug frame at y=296,
 * which lands the pill itself at 279. */
.atlas-slide--closing .clo__cta {
  position: absolute;
  left: 67px;
  top: 279px;
  width: 460px;
  height: 93px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  border: 1.2px solid #FFBB00;
  border-radius: 12px;
  background-image: linear-gradient(
    11.276deg,
    #303946 1%,
    #202833 21.967%,
    #27313D 104.5%
  );
  box-shadow:
    0 12px 16.8px rgba(0, 0, 0, 0.32),
    inset 0 1.2px 14.4px rgba(255, 255, 255, 0.08);
  text-decoration: none;
  pointer-events: auto;
  cursor: pointer;
}

.atlas-slide--closing .clo__cta-label {
  width: 370px;
  font-family: var(--font-family-inspire);
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
  font-size: 33.6px;
  line-height: normal;
  color: #F5F7FA;
  white-space: nowrap;
}

/* ---- divider ------------------------------------------------------- */
.atlas-slide--closing .clo__rule {
  position: absolute;
  left: 63px;
  top: 540px;
  width: 1808px;
  height: 1px;
  background: #C0D1E2;
}

/* ---- appendix rows -------------------------------------------------- */
/* The two title boxes are fixed at their Figma widths rather than hugging
 * their text. That is what keeps both arrows on the x Figma put them on
 * even when the display face falls back to Open Sans and the strings
 * measure differently. */
.atlas-slide--closing .clo__row {
  position: absolute;
  left: 63px;
  display: flex;
  align-items: center;
  height: 36px;
  text-decoration: none;
  pointer-events: auto;
  cursor: pointer;
}

.atlas-slide--closing .clo__row--1 { top: 742px; }
.atlas-slide--closing .clo__row--2 { top: 822px; }

.atlas-slide--closing .clo__num {
  width: 50px;
  font-family: var(--font-family-inspire);
  font-weight: var(--font-heading); /* InspireTWDC Heavy 600 */
  font-size: 36px;
  line-height: 1;
  color: #4F80E0;
}

.atlas-slide--closing .clo__row-title {
  font-family: var(--font-family-inspire);
  font-weight: var(--font-body); /* InspireTWDC Medium 500 */
  font-size: 36px;
  line-height: 1;
  color: #FFFFFF;
}

.atlas-slide--closing .clo__row--1 .clo__row-title { width: 775px; }
.atlas-slide--closing .clo__row--2 .clo__row-title { width: 779px; }

/* ---- arrows --------------------------------------------------------- */
/* One path, two colours. Figma exports it twice; the artwork is identical
 * so it is drawn once and tinted through currentColor. */
.atlas-slide--closing .clo__arrow {
  display: block;
  flex: none;
  width: 36px;
  height: 36px;
}

.atlas-slide--closing .clo__arrow--cta { color: #D6A85F; }
.atlas-slide--closing .clo__arrow--row { color: #4F80E0; }

/* ---- hover and focus ------------------------------------------------ */
/* Restrained on purpose: this is projected, and a link that lifts or
 * glows under the presenter's cursor reads as a bug from the back of
 * the room. Focus uses the same 2px ring the app's buttons use. */
.atlas-slide--closing .clo__cta:hover,
.atlas-slide--closing .clo__row:hover {
  filter: brightness(1.12);
}

.atlas-slide--closing .clo__cta:focus-visible,
.atlas-slide--closing .clo__row:focus-visible {
  outline: 2px solid #4F80E0;
  outline-offset: 6px;
  border-radius: 4px;
}

.atlas-slide--closing .clo__cta:focus-visible { border-radius: 12px; }


/* =====================================================================
   ATLAS - appendix return control
   ---------------------------------------------------------------------
   Deck shell, not slide content. The appendix sections are ordinary
   slides that the main run skips, and neither they nor the Figma closing
   frame should have to grow a back button, so the control lives outside
   the scaled stage and is only rendered while an appendix is open.
   ===================================================================== */
/* Top-left of the navy card, in the slide's own coordinates. Absolute
 * against .atlas-stage, not fixed against the viewport: the stage is the
 * navy card, so "inside it" and "28px from its edges" are the same
 * statement at every scale, and the button can never be pulled back to
 * the bottom by an inherited rule because bottom is never set.
 * 28px clears the card's 40px corner radius comfortably and leaves a gap
 * above the appendix headings, which both start at y=88. */
.atlas-appendix-back {
  position: absolute;
  left: 28px;
  top: 28px;
  z-index: 20;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px 10px 12px;
  border: 1px solid rgba(245, 247, 250, 0.28);
  border-radius: 999px;
  background: rgba(2, 0, 36, 0.82);
  backdrop-filter: blur(6px);
  color: #F5F7FA;
  font-family: "Open Sans", sans-serif;
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
}

.atlas-appendix-back[hidden] { display: none; }

.atlas-appendix-back:hover {
  border-color: rgba(245, 247, 250, 0.5);
  background: rgba(2, 0, 36, 0.94);
}

.atlas-appendix-back:focus-visible {
  outline: 2px solid #4F80E0;
  outline-offset: 2px;
}
