/* ============================================================================
   Signal Markets — host-shell globals
   ============================================================================
   Loaded for product 9 by layout.tsx via /brands/signalmarkets/global.css.

   THIS IS THE SECOND TOKEN SYSTEM, and the easy trap. The shell (Next app:
   header, side menu, bottom tab bar, page chrome) paints from the HSL tokens
   below; the widgets paint from the --color-* and --pm-* tokens in
   theme/tokens.css. The two
   are not wired together, so a brand colour has to be stated in both places —
   `vidamarkets/global.css` says the same thing.

   FORMAT: these are bare HSL triplets, NOT hex and NOT `hsl(...)`. Consumers
   wrap them themselves (`hsl(var(--primary))`, `hsl(var(--primary) / 0.85)`),
   which is what makes the alpha variants possible. Writing a hex here silently
   breaks every one of the 177 call sites in app/.

   SCOPING: `.dark`/`.light` sits on <html> (app-loader.tsx), the brand on
   <body> — so `.dark [data-brand='signalmarkets']` means this brand in dark
   mode. A bare `[data-brand='signalmarkets']` block is theme-independent.

   Dark is the default theme, so fill that block first. Convert each colour from
   the mockup to HSL and drop the `hsl()` wrapper.
   ============================================================================ */

@import url('../base/global.css');

/* ── shell tokens, DARK ──────────────────────────────────────────────────────
   Ten tokens, and `--primary` is the one all five brands always set. Keep them
   in step with theme/tokens.css: --background ↔ S4, --card ↔ S5,
   --primary ↔ S1, --foreground ↔ S9-primary, --border ↔ S8. */
.dark [data-brand='signalmarkets'] {
  /* The scale moved up one step in the redraw (Figma 1006:428045): what used to
     be the card colour is now the page, and cards sit on a new lighter tone.
     Named after the Figma variables so the two can be compared directly. */
  --background: 196 55.6% 5.3%; /* #061115 — the page, teal frame 2132:239955 */
  --card: 199.1 57.9% 7.5%; /* #08171e — header bar, footer, bottom bar */
  --elevated: 240 3.9% 20%; /* S7 #313135 — hover/raised shell surfaces */
  --border: 198 35.7% 16.5%; /* S8 #1b3039 — shell borders */
  --foreground: 204 23.8% 95.9%; /* S9 rank 1 #f2f5f7 — primary shell text */
  --muted-foreground: 205.7 20% 86.3%; /* S9 rank 2 #d5dde3 — secondary shell text;
                                     43 call sites, the most-used shell token */
  --muted: 0 0% 36%; /* S10 #5c5c5c — disabled shell text (derived, see tokens.css) */
  --primary: 166.7 60.5% 46.7%; /* S1 #2fbf9f — brand accent: Sign Up, deposit CTA */
  --primary-foreground: 165.5 74.4% 7.6%; /* #05221b, text ON the accent. NOT
                                    white: S1 is light enough (L 47%) that white
                                    would fail contrast; the teal frame inks
                                    every accent pill with this. */
  --destructive: 0 60.8% 48%; /* S12 #c53030 — logout, error states */

  /* Accent-filled controls (Sign Up, segmented switch). Dark takes the
     gradient, light the flat accent — verified on both header frames
     (436:54798 vs 229:14319). So the gradient is a dark-theme treatment, not
     the accent itself. */
  --accent-cta-bg: var(--accent-gradient);
  --accent-cta-text: var(--sm-accent-ink); /* Deposit, 2132:240111 */

  /* Theme toggle, dark: track is the card surface, the hairline is the mockup's
     own value rather than --border (it reads lighter than the shell's). */
  --theme-toggle-track-light: hsl(var(--card));
  --theme-toggle-track-dark: hsl(var(--card));
  --theme-toggle-track-dark-hover: hsl(var(--card));
  --theme-toggle-border: 1px solid rgba(237, 237, 237, 0.07);
  --theme-toggle-thumb-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
  --theme-toggle-icon: #999999; /* 436:54826 — a step darker than --muted-foreground */

  /* The active half carries the accent gradient here, like the other
     accent-filled controls in dark (436:54826 fills the selected segment with
     the same stops as Sign Up). Only the PAINT is re-pointed —
     --theme-toggle-thumb stays the flat accent below, because the hover ring
     and the focus outline read it as a colour. */
  --theme-toggle-thumb-bg: var(--accent-gradient);
}

/* Bars, dark (2132:240057 header, 2132:243146 mobile bottom bar). The frame
   lifts both off the #061115 page onto the S5 surface. The header paints its
   bar, scroll button and edge fades from `--background` with no token of its
   own, so the bar's ground is restated on the bar rather than on each part. */
.dark [data-brand='signalmarkets'] {
  --pm-bottom-toolbar-bg: hsl(var(--card));
}
.dark [data-brand='signalmarkets'] .header-container {
  --background: var(--card);
}

/* Nav menu, dark (desktop dropdown 229:26211 / 229:26047, mobile drawer
   589:132054). The shared menu resolves every colour through these, so this
   block is the whole skin — no structural rule from side-menu.css is restated:
   220px width, 8px padding, 12px radius and the dashed group rule are already
   what the frame draws.

   On `.side-menu`, not the brand root: side-menu.css declares these same
   properties on `.side-menu` itself (its lines 14-26), and a declaration
   matching the element beats a value inherited from an ancestor at any
   specificity — so a body-scoped override never reaches the panel. That sheet
   documents `[data-brand=…] .side-menu` as the override API, and caliente, vsin
   and vidamarkets all scope it that way. Both shells carry the class:
   `MenuDropdown.tsx`, and `MenuDrawer.tsx` where `rootClassName` lands on
   `.ant-drawer`.

   Only dark is stated. The mockup covers this theme; light falls through to the
   shell defaults (--card / --foreground / --border), which resolve to this
   brand's own light palette. */
.dark [data-brand='signalmarkets'] .side-menu {
  --side-menu-bg: #10232c; /* desktop dropdown 2209:93261 */
  --side-menu-text: #f2f5f7;
  --side-menu-text-muted: #d5dde3;
  --side-menu-divider: #1b3039;
  /* Hover, and the active row's fill in the frame — one value for both. */
  --side-menu-item-hover-bg: #16303c;
  /* Logout. NOT --destructive (#c53030), which is the shell's error red. */
  --side-menu-danger: #f54043;

  /* The rule under the header. Stated whole per theme rather than as a colour
     token: two declarations beat two tokens for one line, and the shorthand's
     `none` default is what keeps every other brand from gaining a pixel. */
  --pm-header-border-bottom: 1px solid #1a1f24;
}

/* The mobile drawer (2132:729) sits on the page-surface rung and lifts the
   active row to #10232c; the desktop dropdown above is one rung higher. */
.dark [data-brand='signalmarkets'] .side-menu--drawer {
  --side-menu-bg: #08171e;
  --side-menu-item-hover-bg: #10232c;
}

/* ── shell tokens, LIGHT ─────────────────────────────────────────────────────
   Sampled from the light frame, not derived by inverting the dark one. Anything
   left unset falls back to the base light palette in brands/base/global.css. */
.light [data-brand='signalmarkets'] {
  /* Light inverts with the same redraw (Figma 1181:73974): the page is now pure
     white and the cards a hair off it, where before it was the other way round
     and they separated by border alone. */
  --background: 0 0% 100%; /* background/surface 2 #ffffff — the page */
  --card: 0 0% 97.3%; /* background/elevated #f8f8f8 — shell cards */
  --elevated: 0 0% 90%; /* S7 #e6e6e6 */
  --border: 0 0% 93%; /* S8 #ededed */
  --foreground: 0 0% 5%; /* S9 rank 1 #0d0d0d */
  --muted-foreground: 0 0% 25%; /* S9 rank 2 #404040 */
  --muted: 0 0% 64%; /* S10 #a3a3a3 */
  --primary: 166.7 60.5% 46.7%; /* S1 #2fbf9f — the accent is the same in both themes */
  --primary-foreground: 0 0% 0%; /* same reasoning as dark */
  --destructive: 0 60.8% 48%; /* S12 #c53030 — unchanged across themes */

  --accent-cta-bg: hsl(var(--primary)); /* flat #2fbf9f per 229:14319 */
  --accent-cta-text: hsl(var(--foreground)); /* #0d0d0d, not the dark #000 */

  /* Theme toggle, light (229:14193 / 231:28059). The active half is a FLAT
     accent here, not the gradient — `--accent-cta-bg` already makes that split
     per theme, the same one the primary CTA makes, so the gradient reads as a
     dark-theme treatment rather than as the accent itself.

     Track is #f5f5f5 in the mockup, which is Figma's own
     `surface/surface-secondary`; folded into S6 (#f2f2f2 — three units off and
     invisible) instead of becoming a fourth grey. Thumb radius is 40 there vs
     50 in dark; both clamp to a circle on a 28px box, so one value is kept. */
  --theme-toggle-track-light: #f2f2f2;
  --theme-toggle-track-dark: #f2f2f2;
  --theme-toggle-track-dark-hover: #f2f2f2;
  --theme-toggle-border: 1px solid hsl(var(--border));
  --theme-toggle-thumb-shadow: 0 2px 2px rgba(0, 0, 0, 0.08);
  --theme-toggle-icon: #666666; /* 229:14193 */

  /* Seven units off this brand's --color-PrimaryBorder (#ededed), so it is
     stated rather than rounded to the role. */
  --pm-header-border-bottom: 1px solid #e6e6e6;
}

/* ── theme-independent shell values ──────────────────────────────────────────
   LAYOUT.bottomTabBar is true for this brand, so the 68px reserve from
   brands/base is correct and is NOT zeroed here (contrast vidamarkets, which
   runs without a bottom bar and has to drop it). */
[data-brand='signalmarkets'] {
  /* THE ACCENT HAS ITS OWN TOKEN, shaped as a gradient. It cannot live in
     --primary like every other brand's accent (the gradient form is not an HSL
     triplet), and the controls that paint it are re-pointed to it in
     theme/components/header.css.

     Frame 2132-238483 draws these controls as flat teal, so both stops are the
     flat accent; the token keeps the gradient shape so those controls keep
     working and a gradient can come back by changing this one line.

     --primary stays a flat colour: borders, focus rings and text still need a
     single value, and `hsl(var(--primary))` has 21 call sites that a gradient
     would break. */
  --accent-gradient: linear-gradient(#2fbf9f, #2fbf9f);

  /* Theme toggle — `themeToggle.variant: thumbSwitch`, restyled from the
     segmented control in the Figma header (436:54826). Geometry only here; the
     colours are per-theme below. The shared control in
     theme-toggle/theme-toggle.css owns the structure, so this brand reassigns
     its tokens rather than duplicating any rule.

     Box maths, outside in: 1px border + 1px inset + 28px thumb + 2px gap +
     28px thumb + 1px inset + 1px border = 62 wide, 32 tall — the frame's own
     size. The inset is 1px, not the 2px the frame's padding reads, because
     Figma strokes the frame on the INSIDE: its 2px padding is measured from
     the outer edge and the stroke sits within it, while a CSS border adds to
     whatever padding follows. 1 + 1 puts the thumb's left edge at x=2 from the
     outer edge, which is where the frame draws it. Travel stays thumb + gap
     (30px) and lands the thumb with the same 1px inset on the right. */
  --theme-toggle-width: 62px;
  --theme-toggle-height: 32px;
  --theme-toggle-padding: 1px;
  --theme-toggle-thumb-size: 28px;
  --theme-toggle-travel: 30px;
  --theme-toggle-radius: 83px;
  --theme-toggle-icon-size: 16px;

  /* The flat accent — S1 #2fbf9f, identical in both themes, hence stated once
     here rather than per theme. This is what LIGHT paints the active half with
     (229:14193), and in BOTH themes it is the colour the hover ring and the
     focus outline are derived from; dark re-points only the paint, via
     --theme-toggle-thumb-bg above. `--primary-foreground` is `0 0% 0%` in both
     themes precisely because it means "on the accent". */
  --theme-toggle-thumb: hsl(var(--primary));
  --theme-toggle-icon-active: hsl(var(--primary-foreground));

  /* All Markets heading (1006:428035 dark / 1181:73964 light). 24/32 at
     -0.72px. Identical in both themes — the frames differ only in colour, and
     that already comes from --foreground (#fff / #0d0d0d), so nothing is
     restated per theme. The section is the first thing under the hero and the
     hero carries no bottom spacing of its own, hence the 32px here. */
  --am-title-size: 24px;
  --am-title-weight: 600;
  --am-title-line-height: 32px;
  --am-title-tracking: -0.03em; /* -0.72px at 24px */
  /* Negative: the section is pulled UP into the hero's bottom padding, which
     is 95px and drawn over by the backdrop. 32 was my own reading of the gap
     and it was wrong — this is measured off the composition. */
  --am-section-margin-top: -38px;

  /* Buttons to the marquee strip below them (1006:395055 ends at y=480, the
     strip opens at 556). Stated as the gap the frame draws; hero.module.css
     nets off what `.copy`'s own 24px `gap` already contributes. */
  --pm-hero-marquee-gap: 76px;

  /* No gutter between the hero's two columns. Each is a fixed share and the
     card carries its own outer ring (--pm-slide-container-padding), so a gap
     here would sit on top of that rather than replace it. */
  --pm-hero-gap: 0;

  /* Between the page's own sections; the shell ships 20. */
  --pm-page-gap: var(--pm-space-6); /* 24px */

  /* 1344, not the frame's 1312, because `.page` is border-box and carries a
     16px inset: 1344 − 32 = the 1312 content the frame draws, and at its 1440
     the gutter computes to (1440 − 1344) / 2 + 16 = 64, which is the frame's
     own padding. Setting 1312 here would cap the CONTENT at 1280. */
  --pm-page-max-width: 84rem; /* 1344px */

  /* One inset for the page, the header and the category bar, so their content
     edges line up. The shell's three defaults disagree (16 / 0 / 24). */
  --pm-page-padding-inline: var(--pm-space-4); /* 16px */

  /* …and none on the rows inside the header, which each carry 24 by default.
     `.header` owns the inset now, so the rows would only double it. Their
     vertical padding is untouched. */
  --pm-header-row-padding-inline: 0;

  /* The page opens flush under the header — its own rule below is what
     separates the two. */
  --pm-page-padding-block-start: 0;
  /* Read by `.header-layout__content`, which is NOT inside the hero — this sat
     in the hero block and never reached it. This brand draws no bottom
     toolbar, so there is nothing to clear and 8 is simply the gap to whatever
     the layout puts after the page. */
  --pm-header-layout-content-padding-bottom: var(--pm-space-2); /* 8px */
}

/* Hero copy and actions, mobile (1006:491586 / 1006:491589). The headline
   drops to 36/44 from the desktop 48/56, the pair below it tightens to 8, and
   the body copy takes `text/secondary` where desktop uses the tertiary grey.

   The buttons stop sizing to their labels and split the row in half — the
   frame gives both `flex: 1 0 0` across 328, which is the one change that is
   not a token. */
@media (max-width: 900px) {
  [data-brand='signalmarkets'] {
    --pm-hero-title-size-mobile: 2.25rem; /* 36px */
    --pm-hero-title-leading-mobile: 2.75rem; /* 44px */
    --pm-hero-lede-gap: var(--pm-space-2); /* 8px; desktop 12 */
    --pm-hero-subtitle-size: 0.875rem; /* 14px; desktop 16 */
    --pm-hero-subtitle-leading: 1.375rem; /* 22px; desktop 24 */
    --pm-hero-subtitle-tracking: -0.03em; /* -0.42px at 14px */
    --pm-hero-copy-gap: var(--pm-space-4); /* 16px; desktop 24 */
    /* No bottom padding: stacked, the section ends with the card and the page
       gap below carries the separation. The shell ships 24/0/32. */
    --pm-hero-padding-mobile: var(--pm-space-6) 0 0;
    --pm-hero-gap-mobile: var(--pm-space-4); /* 16px between copy and card */
    /* Level with the rows above it, so the strip gains no margin of its own:
       hero.module.css nets this against --pm-hero-copy-gap, and equal values
       net to zero. Desktop pulls it 76 down instead. */
    --pm-hero-marquee-gap: var(--pm-space-4);
    --pm-hero-action-inline-start: var(--pm-space-4); /* 16px */
    --pm-hero-action-inline-end: var(--pm-space-4);
  }

  /*
   * Two values that have to be stated ON `.lobby__hero`, not on the brand root
   * with the rest of the block above — and the distinction is not cosmetic.
   *
   * The dark and light hero blocks further down declare both names on the hero
   * element itself. A custom property resolves from the NEAREST declaring
   * ancestor, so anything stated on the body is shadowed for every element
   * inside that hero, at any specificity and in any media query. Measured at
   * 374px: the subtitle rendered #999999 against the #b3b3b3 written here, and
   * the CTA rendered the desktop 6px blur against the 3px written here.
   *
   * Dark only, for the same reason the blocks below split: the mobile frame is
   * the dark one, #b3b3b3 would be unreadable on light's white, and light draws
   * no CTA shadow at all. Stated on the body, the 3px blur was reaching light
   * mobile — dead where it was meant and live where it was not.
   */
  .dark [data-brand='signalmarkets'] .lobby__hero {
    --pm-hero-subtitle-color: var(
      --pm-text-secondary-redraw
    ); /* text/secondary, not the tertiary grey */
    /* Half the desktop blur — 1006:491590 draws 3 where the wide frame draws 6. */
    --pm-hero-action-primary-shadow: 0 2px 3px 0 rgb(0 0 0 / 0.08);
  }

  /* Half the row each, stated rather than derived.

     Both simpler forms land a few pixels off, and for the same underlying
     reason: these two buttons are inset differently (16 against 20), and
     neither growing nor shrinking is blind to that. `flex: 1 0 0` splits the
     free space evenly and then adds each padding on top — 8 apart. `width:
     100%` with the default shrink is weighted by the CONTENT box, so the one
     with more padding shrinks less — 4 apart. Only a stated basis is exact.

     `min-width: 0` lets a button go under its own label, which the default
     `min-width: auto` forbids. `nowrap` because they are meant to shrink
     together, not drop onto a second line. */
  [data-brand='signalmarkets'] {
    --pm-hero-actions-wrap: nowrap;
  }

  [data-brand='signalmarkets'] .lobby__hero-action {
    flex: 0 1 calc(50% - var(--pm-hero-actions-gap, 8px) / 2);
    min-width: 0;
  }

  /* The secondary pill is inset wider than the primary, which carries a glyph. */
  [data-brand='signalmarkets'] .lobby__hero-action--secondary {
    padding-inline: var(--pm-space-5); /* 20px */
  }
}

/* The header's theme toggle is hidden below the width where the category strip
   starts to crowd it. Nothing is lost: the side menu carries its own toggle
   (LAYOUT.menu.showThemeToggle), and on mobile the menu is where the header
   sends people anyway. 640 is the header's own breakpoint. */
@media (max-width: 639.98px) {
  [data-brand='signalmarkets'] {
    --pm-header-bar-right-display: none;
    /* The frame gives 116.758 x 22.009; the width follows from the ratio, so
       only the height is stated. 22 rather than 22.009 — the width then lands
       at 116.70 against 116.758, which is under a tenth of a pixel. */
    --pm-header-logo-height: 22px;

    /* Mobile bottom bar (the frame's 393-wide strip). Its fill is lifted onto
       the S5 card surface by the dark block further down (--pm-bottom-toolbar-bg),
       so only the rule above it and the lift are stated here. The bottom 24 is the drawn inset; the shell
       floors it against the home indicator, so a device with one gets more. */
    --pm-bottom-toolbar-border: hsl(var(--border));
    --pm-bottom-toolbar-shadow: 0 -1px 12px 0 rgb(0 0 0 / 0.04);
    --pm-bottom-toolbar-justify: center;
    --pm-bottom-toolbar-align: flex-start;
    --pm-bottom-toolbar-gap: var(--pm-space-1); /* 4px */
    --pm-bottom-toolbar-padding-top: var(--pm-space-2); /* 8px */
    --pm-bottom-toolbar-padding-inline: var(--pm-space-2); /* 8px */
    --pm-bottom-toolbar-padding-bottom: var(--pm-space-6); /* 24px */

    /* Each tab is an equal share with its own hit area, so the row centres as
       a whole while the tabs still divide it. */
    --pm-bottom-toolbar-item-flex: 1 0 0;
    --pm-bottom-toolbar-item-padding: 6px var(--pm-space-3); /* 6/12 */
    --pm-bottom-toolbar-item-gap: 6px; /* off the scale */
    --pm-bottom-toolbar-item-radius: var(--pm-space-4); /* 16px */

    /* 11/16 with -0.33px, and the icon grey rather than the caption one —
       --pm-icon-muted is the same #cccccc the search loupe and the category
       chevrons take, and it flips with the theme. */
    --pm-bottom-toolbar-label-size: 0.6875rem; /* 11px */
    --pm-bottom-toolbar-label-line: 1rem; /* 16px */
    --pm-bottom-toolbar-label-tracking: -0.03em; /* -0.33px at 11px */
    --pm-bottom-toolbar-label-color: var(--pm-icon-muted);
    --pm-bottom-toolbar-label-features:
      'ss11' on, 'cv09' on, 'liga' off, 'calt' off;

    /* Scroll chevrons: 42 tall with 11 above and below the glyph, and 20 on
       the side facing the tabs — mirrored, so each pads away from its own
       edge. `width: auto` because the padding decides it; the shell's 28 was
       a square that 20 of padding would have left 8px of. */
    --pm-header-scroll-btn-width: auto;
    --pm-header-scroll-btn-height: 42px;
    --pm-header-scroll-btn-padding-block: 11px;
    --pm-header-scroll-btn-gutter: var(--pm-space-5); /* 20px */
    /* Flush with the strip's own edge: the gutter above is what holds the
       glyph off the tabs, so an inset on top of it would double the offset. */
    --pm-header-scroll-btn-inset: 0;
    /* The shell's 50% draws a circle around a 28px square; on a box that is 42
       tall and as wide as its padding makes it, the same 50% is an ellipse.
       The frame draws no rounding at all. */
    --pm-header-scroll-btn-radius: 0;

    /* 20 square, against the shell's 14. --pm-icon-muted rather than a hex:
       it is the same grey the search loupe takes, and it flips with the theme
       (#cccccc dark, #666666 light) where a literal would not. */
    --pm-header-scroll-icon-size: var(--pm-space-5); /* 20px */
    --pm-header-scroll-icon-color: var(--pm-icon-muted);
    --pm-header-scroll-icon-color-hover: var(--color-PrimaryText);

    /* The fade ramps across the whole strip rather than reaching ground at 48
       of 72. The two ENDS are deliberately left on the page ground: the frame
       states #0f1419, which is what surface 2 held BEFORE the redraw moved it
       to #1b2025 (see --color-BasicBg) — so pinning it would lay a darker
       smear over a lighter bar. Flagged rather than guessed.
       `backdrop-filter: blur(0px)` from the same node is a no-op Figma emits
       for every layer and is not carried. */
    --pm-header-scroll-fade-stop: 100%;
  }
}

/* Bottom bar states (2132:235915): the current tab's label and glyph take the
   accent, the rest the icon grey. The glyphs are CMS <img> files in one grey,
   so the colour is cast as a shadow of the image shifted out of its clipped
   box — exact, and it keeps the file's alpha. The live tab's glyph takes the
   same cast (its file is black like the others) but never the accent: the
   widget keeps its own live treatment, and its label is already grey. The
   shift is the shell's 24px `.bottom-toolbar__icon` width, stated once. */
@media (max-width: 639.98px) {
  [data-brand='signalmarkets'] {
    --sm-bottom-glyph-shift: 24px;
  }

  [data-brand='signalmarkets'] .bottom-toolbar__icon {
    overflow: hidden;
  }

  [data-brand='signalmarkets'] .bottom-toolbar__icon img {
    transform: translateX(calc(-1 * var(--sm-bottom-glyph-shift)));
    filter: drop-shadow(var(--sm-bottom-glyph-shift) 0 0 var(--pm-icon-muted));
    /* Without this iOS Safari can skip the shadow and paint no glyph. */
    will-change: transform, filter;
  }

  [data-brand='signalmarkets']
    .bottom-toolbar__item[aria-current='page']:not([data-id='live'])
    .bottom-toolbar__icon
    img {
    filter: drop-shadow(
      var(--sm-bottom-glyph-shift) 0 0 var(--color-PrimaryBtn)
    );
  }

  [data-brand='signalmarkets']
    .bottom-toolbar__item[aria-current='page']:not([data-id='live'])
    .bottom-toolbar__label {
    color: var(--color-PrimaryBtn);
  }
}

/* How-it-works, row (1006:429107). Box, paddings, lede width, step padding,
   gaps and the 130px/10% numeral are already the component's defaults and the
   frame's values; what the frame draws differently is stated here: the card is
   the full 1312 container (kept 16 off the viewport edge below it), the type
   runs a touch tighter, and the body copy sits on a 1.55 line. */
[data-brand='signalmarkets'] .faq-numbered {
  --faq-numbered-max-width: min(1312px, calc(100% - var(--pm-space-8)));
  --faq-numbered-title-tracking: -0.32px;
  --faq-numbered-description-line: normal;
  --faq-numbered-description-tracking: -0.02em; /* -0.24px at 12px */
  --faq-numbered-question-tracking: -0.02em; /* -0.3px at 15px */
  --faq-numbered-answer-line: 1.55;
  --faq-numbered-answer-tracking: -0.02em; /* -0.23px at 11.5px */
}

/* How-it-works, stacked (1006:492238): banner padding 16, title to subtitle 8,
   the title block to the steps 24, 8 between steps, a hairline above steps 2
   and 3. */
@media (max-width: 900px) {
  /* The slot's own values go on the BRAND ROOT, not on the card below — the
     slot is the card's PARENT, and a custom property inherits down, never up.
     Stated on `.faq-numbered` they were invisible to the element that reads
     them, which is the same trap --footer-icon fell into. */
  [data-brand='signalmarkets'] .faq-numbered {
    --faq-numbered-lede-gap: var(--pm-space-2); /* 8px; the row uses 12 */
    --faq-numbered-steps-gap: var(--pm-space-2); /* 8px between steps */
    --faq-numbered-step-gap: var(--pm-space-1); /* 4px, question to answer */
    /* The stack is full-bleed inside the slot; the 24 the frame leaves between
       the title block and the steps is the lede's own bottom padding. */
    --faq-numbered-margin: var(--pm-space-6) auto; /* 24 above and below */
    --faq-numbered-max-width: calc(100% - var(--pm-space-8)); /* 16 a side */

    /* Sub-copy: 14/19.6 where the row framing draws 12. Colour is NOT here:
       the theme blocks below declare it on the same element and come later in
       the file, so a value written here never lands. */
    --faq-numbered-description-size: 0.875rem; /* 14px */
    --faq-numbered-description-line: 1.4; /* 140% = 19.6px */
    --faq-numbered-description-tracking: -0.02em; /* -0.28px at 14px */

    /* Step titles 16 and copy 12, where the row draws 15 and 11.5. */
    --faq-numbered-question-size: 1rem;
    --faq-numbered-question-tracking: -0.02em; /* -0.32px at 16px */
    --faq-numbered-answer-size: 0.75rem; /* 12px */
    --faq-numbered-answer-tracking: -0.02em; /* -0.24px at 12px */

    /* 20 above and below each step; the first starts at 16 and the last ends
       at 8 (rules below). The rule between steps is `border/default`, not the
       row's `border/input`. */
    --faq-numbered-step-padding: var(--pm-space-5) 0;
    --faq-numbered-divider-stacked: var(--faq-numbered-border);

    /* The ghosted ordinal, drawn bigger and fainter than the row framing's:
       110 against 130, 8% against 10%, and pulled up out of the step. */
    --faq-numbered-numeral-size-sm: 110px;
    --faq-numbered-numeral-opacity: 0.08;
    --faq-numbered-numeral-top: -15px;
    /* The card's own 16 is the inset between the border and the copy. */
    --faq-numbered-padding: var(--pm-space-4); /* 16px */
  }

  [data-brand='signalmarkets'] .faq-numbered__step:first-child {
    padding-top: var(--pm-space-4); /* 16px */
  }

  /* The last step ends the card, so it does not need the 20 that separates one
     step from the next — 8 is what the frame leaves before the card's own
     bottom inset takes over. */
  [data-brand='signalmarkets'] .faq-numbered__step:last-child {
    padding-bottom: var(--pm-space-2); /* 8px */
  }
}

/* Mobile: All Markets sits UNDER the hero instead of being pulled into its
   bottom padding, so the negative desktop value has to go. `.page` spaces its
   children with `gap`, which is 24 here and which no child can override, so
   the margin is the difference up to the 32 the mobile frame draws.

   768px is the breakpoint page.module.css already treats as mobile. */
@media (max-width: 767.98px) {
  [data-brand='signalmarkets'] {
    --am-section-margin-top: var(
      --pm-space-2
    ); /* 8px; 24 + 8 = the frame's 32 */
  }
}

/* ── lobby hero (PRED-411) ───────────────────────────────────────────────────
   Figma: 1006:395055 (elements), 1006:395059 (buttons, light), 1006:427901
   (buttons, dark). `hero.module.css` owns the geometry and stays brand-neutral,
   so everything here is a token reassignment reached through the `.lobby__hero`
   hook.

   Two of the frames' variables land exactly on tokens this brand already has,
   in BOTH themes, so they are reused rather than restated:

     text/primary          #0d0d0d / #ffffff   = --foreground
     background/surface 2  #ffffff / #1b2025   = --background

   The rest have no counterpart and are stated per theme below. `--muted-
   foreground` (#404040 / #bebebf) is close to the frames' body greys and is
   deliberately NOT used for them: close is not the same, and rounding a value
   onto a token that means something else is how a palette drifts. */
[data-brand='signalmarkets'] .lobby__hero {
  /* The featured card's fill — ONE declaration for both of its renderings.
     The default slide draws `.pm-slide-card` and the crypto slide replaces
     that shell with `.up-down-crypto-slide`, so the same card arrives under
     two class names; theme/components/lobby-top-row.css points the widget's
     own --up-down-slide-bg at this, and neither can drift from the other.

     `background/surface 2` — the PAGE tone, not the elevated one the shell's
     cards moved to. The frames give this card the same fill as the ground it
     sits on and separate it with the border and glow alone, so it reads as lit
     rather than raised.

     It has to be STATED, not left to the `var()` in the rule below: undefined,
     `background: var(--pm-hero-card-bg)` is invalid at computed-value time and
     the card falls back to `transparent` — which over a #1b2025 page looks
     identical until you notice the hero's backdrop showing through the card. */
  --pm-hero-card-bg: hsl(var(--background));

  /* The backdrop is the client's wave (2187:160166), painted by the `::before`
     rule below. The circle-era mesh <img> is gone (the seed ships no
     `hero.backdrop`), and so is the green bloom: size 0 renders no box. */
  --pm-hero-glow-size: 0;

  /* Frame height of the wave (1440 x 700). `min-height`, so copy that runs
     longer still pushes the block open. */
  --pm-hero-min-height: 700px;
  /* The wave bleeds past the 1312 content column to the viewport edges;
     `.header-layout` clips the page sideways, so nothing scrolls. Both axes,
     because one `hidden` axis computes the other to `auto` and clips it. */
  --pm-hero-overflow-x: visible;
  --pm-hero-overflow-y: visible;

  /* The card is a fixed 770 drawing (1006:395067), not a share of the row, so
     it caps there rather than growing. With the copy column at 460 and no
     gutter, 460 + 770 fits the 1312 content width with room left.

     It DOES shrink, though. `flex-shrink: 0` held 770 below the width where
     that sum stops fitting, and the row overflowed instead of the card giving
     ground — the copy column has a measure it cannot go under, so the card is
     the half that yields. -max-width keeps 770 the ceiling either way. */
  --pm-hero-featured-grow: 0;
  --pm-hero-featured-shrink: 1;
  --pm-hero-featured-basis: auto;
  --pm-hero-featured-width: 100%;
  --pm-hero-featured-max-width: 770px;
  /* …and the copy column absorbs what the fixed card leaves: 1312 - 770 = 542
     against its 460 basis. */
  --pm-hero-copy-grow: 1;

  /* The frame's 484. The widget writes this token as `height` on
     `.pm-slide-container` (market-slide-card.css:22) at every width, so the
     default slide's card is exactly this tall. It ships 380 and floors the
     card at 370, so both were short. */
  --pm-slide-height: 484px;
  /* And no ring around it: the widget spaces the container off its neighbours
     by 8, which here only insets the card inside a column already sized to it. */
  --pm-slide-container-padding: 0;
  /* Fill that floor rather than sizing to content, and grow with it when a
     slide runs longer. The card's base rule is already `height: 100%`, but the
     desktop container query replaces it with `auto` over a 370 floor — this is
     the token that rule exposes for exactly this case. */
  --pm-slide-card-height: 100%;

  --pm-hero-title-color: hsl(var(--foreground));
  --pm-hero-subtitle-measure: 406px;
  /* `background/surface-2` in the frame (1006:491592), which is --background
     here. Not --card, `background/elevated`, which this took and which is
     #20282c against the role's #1b2025. */
  --pm-hero-action-primary-text: hsl(var(--background));

  /* The accent run is a gradient clipped to the glyphs, and the fill is what
     reveals it. Both are defaulted here rather than only per theme below:
     <html> carries no theme class until hydration, and a fill with no
     gradient is an invisible headline. */
  --pm-hero-accent-fill: transparent;
  --pm-hero-accent-gradient: var(--accent-gradient);
}

/* Stacked (900px is where `hero.module.css` stacks the columns), the wave is
   restated as a fixed 717 x 363 box under the card, as the mobile frame has it. */
@media (max-width: 900px) {
  [data-brand='signalmarkets'] .lobby__hero {
    /* The wave sits under the card, 717 x 363 at x -324 / y 411 on the 393
       screen (2187:160171): flush to the right edge, 26 above the card's top
       (437). Offsets are relative to `.hero`, which sits in `.page`'s 16px
       gutter: x -340, y 289 (411 - 122, anchored to the card, not the header). */
    --pm-hero-wave-width: 717px;
    --pm-hero-wave-height: 363px;
    --pm-hero-wave-left: -340px;
    --pm-hero-wave-top: 289px;
    --pm-hero-wave-fade-end: transparent;
    --pm-hero-wave-ground: transparent;
    --pm-hero-min-height: 608px;

    /* No bloom. */
    --pm-hero-glow-size: 0;

    /* And the section stops cutting the drawing on either axis.

       Down: 1006:491437 runs to 774 while the hero ends around 722, so `hidden`
       drew a hard line across the backdrop some 50px early.

       Sideways: the frame bleeds to the screen edge, but `.hero` sits inside
       `.page`'s gutter, so clipping ON the box left a strip of bare background
       down each side. `overflow-x: clip` with `overflow-clip-margin` was meant
       to push that cut back out to the viewport and does not: the margin is
       honoured only when the box clips on BOTH axes, and this one is
       deliberately open downwards — so the clip stayed on the box and the
       strips stayed with it.

       So the hero clips on neither axis and the bleed is cut where it was
       always going to be cut: `.header-layout` already carries `overflow-x:
       clip` (header-layout.css), one box around the whole page, so the 833px
       backdrop in a 361px hero lands on the viewport edge and the page still
       gets no horizontal scrollbar. One clip for the page beats a per-section
       one that has to be pushed back out to the same place.

       `visible`, not `initial`: a CSS-wide keyword in a custom property makes
       it the guaranteed-invalid value, so `--pm-hero-overflow-x: initial`
       would send `var(--pm-hero-overflow-x, hidden)` to its FALLBACK and clip
       harder than before. */
    --pm-hero-overflow-x: visible;
    --pm-hero-overflow-y: visible;

    /* The tablet range (card 640 and up, still stacked under the copy): the
       frame's 404 (1006:491618: 16 + 26 top row + 42 asset header + 180 chart
       + 88 trade + 16). The widget writes this token as `height` on
       `.pm-slide-container`, the DEFAULT slide's wrapper (the crypto slide
       mounts no container and never reads it), so the card is a fixed 404 and
       `.pm-slide-left` scrolls what does not fit, as it always has. Under 640
       the card is taller because its chart is back: see the next block. */
    --pm-slide-height: 404px;
  }

  /* Phone: the default card with its chart (PRED-893 item 3). A FIXED height,
     so the hero keeps its size when the carousel rotates — the card used to
     take the height of whichever slide showed, and the page below it moved on
     every rotation. Sized for the fullest slide, which then fits without
     scrolling; a shorter one keeps the same box, with the footer pinned to its
     bottom (`margin-top: auto`).

     Measured on the widget's own CSS (plaee-frontend-new#3366 head:
     market-slide-card.css, price-chart.css), in a harness at the 361px card of
     a 393px screen with this file loaded: Inter, 4 outcome rows (the widget's
     default limit), a two-line title, a legend wrapped onto two lines.
       header   12 top padding + 36 title (2 lines at 18)            =  48
       chart    legend 44 + 12 gap + 160 canvas                      = 216
                (one legend line is 18, so 190)
       gap      20, --pm-slide-content-gap's default                 =  20
       outcomes 4 rows x 40 (24 icon + 2 x 8 padding) + 4 above
                the table                                            = 164
       gap      12 between the outcomes and the footer               =  12
       footer   31.8 as rendered (28 min-height)                     =  32
       bottom   12, --pm-slide-content-padding-block-end's default   =  12
       borders  1 + 1                                                =   2
                                                              total  = 506
     Rendered at 506, the fullest slide fills it to the pixel with nothing
     to scroll; with fewer rows or a one-line title or legend the box stays
     506 and the footer sits at its bottom. A third legend line would scroll
     the outcomes by about 26, as the old fixed 404 did.

     The crypto slide never reads this token: its card is content-sized, 404
     in the frame, so it is shorter than this one and the hero still changes
     height when it rotates between a crypto and a default slide. */
  @media (max-width: 689px) {
    [data-brand='signalmarkets'] .lobby__hero {
      /* 671 = the 640px card breakpoint + the page's 16px gutter on each
         side, the card being the hero's full width; + 18 for a classic
         scrollbar, which this media query counts and the card's container
         query does not. Erring wide only makes a two-column card 506. */
      --pm-slide-height: 506px;
    }
  }

  /* ── The default card's chart, stacked (PRED-893 item 3) ─────────────────
     1006:491618 draws the featured card with its chart, and the widget agrees
     for the crypto slide, which renders its chart at every width. The DEFAULT
     slide (`MarketSlideCard`, any non-crypto featured event: politics,
     economics) does not: market-slide-card.css hides `.pm-slide-right` — the
     chart column — below a 640px CARD width, a container query on
     `.pm-slide-container`, and only its two-column layout shows it. On dev the
     slider feeds this brand non-crypto events, so the hero card is the default
     one. Before this reveal it landed with no chart in a mostly empty 404px
     box; with the chart back, the stacked card is 506px (see above).

     The chart is in the DOM at every width, so this is a brand-side reveal.
     `@container`, not `@media`, so the reveal fires on exactly the condition
     the widget hides it on — a card under 640 — whatever the page gutter is.

     Audit against the widget (brand-skin step 3): the chart height and the
     price scale are tokens and are reassigned below. Everything else here
     overrides a literal the widget hardcodes; each such line names where. */
  @container (width < 640px) {
    [data-brand='signalmarkets'] .lobby__hero .pm-slide-right {
      /* no token upstream: market-slide-card.css:81 (`display: none`) */
      display: flex;
      flex-direction: column;
      /* no token upstream: DOM order, market-slide-card.tsx:321/413 — the
         outcomes come first because on desktop the chart is the right column.
         Stacked, the frame puts the chart under the header, above them, where
         the crypto slide draws it. */
      order: -1;
      /* no token upstream: market-slide-card.css:81 has no padding here, and
         --pm-slide-right-padding-inline-end is read only inside
         `@container (min-width: 640px)` (:347), right side alone. The frame's
         chart sits 16 in from both card edges (Chart Container, 328 of 360). */
      padding-inline: var(--pm-space-4);
    }

    [data-brand='signalmarkets'] .lobby__hero .pm-slide-chart-wrap {
      /* no token upstream: market-slide-card.css:87 (`aspect-ratio: 21 / 9`).
         The canvas is lightweight-charts, sized by the height token below, so
         a 21:9 box under a taller canvas overflows onto the outcomes. Auto
         makes the wrap take the canvas at its own height; the widget's 100px
         floor (:88) is under any chart this draws, so it stays. */
      aspect-ratio: auto;
      /* The mini chart itself: the slide reads these two off this wrap
         (market-slide-card/use-slide-chart-tokens.ts, plaee-frontend-new
         #3366) and passes them to PriceChart as `height` / `hidePriceScale`.
         px or a bare number only, `rem` is not parsed. Frame 1006:491618: a
         short chart with no price scale. Bundles that predate the tokens
         ignore them and keep drawing 234px with the scale, so this is safe to
         ship first. */
      --pm-slide-chart-height: 160px;
      --pm-slide-chart-price-scale: hidden;
    }
  }
}

/* The trailing glyph. A mask rather than an <img> so it takes the label's
   colour, which flips with the theme; the asset is the frame's own export. */
[data-brand='signalmarkets'] .lobby__hero-action--primary::after {
  content: '';
  width: 18px;
  height: 18px;
  flex: none;
  background-color: currentColor;
  -webkit-mask: url('./theme/arrow-right.svg') no-repeat center / contain;
  mask: url('./theme/arrow-right.svg') no-repeat center / contain;
}

/* Hover. The shell fades both actions to 90% and antd's global `a:hover`
   repaints the label in its link blue, so the teal pill went pale with a navy
   label. Both buttons keep their fill and label and only lighten, the same
   hover the header's Sign Up has. */
[data-brand='signalmarkets'] .lobby__hero-action {
  transition:
    filter 160ms ease-out,
    transform 160ms ease-out,
    background-color 160ms ease-out;
}

[data-brand='signalmarkets'] .lobby__hero-action--primary:hover {
  color: var(--pm-hero-action-primary-text);
  opacity: 1;
  filter: brightness(1.08);
}

[data-brand='signalmarkets'] .lobby__hero-action--secondary:hover {
  color: var(--pm-hero-action-secondary-text);
  opacity: 1;
  filter: brightness(1.25);
}

[data-brand='signalmarkets'] .lobby__hero-action:active {
  transform: scale(0.97);
}

/* The hero wave (2187:160166 desktop, 2187:160171 mobile). The frame's own
   export, served by the brand rather than uploaded to the CMS. Dark only: the
   frame has no light variant, so light draws the plain page.

   `::before` (the module owns `::after` for the bloom) at z-index 0, under the
   copy and the card. Desktop is full-bleed 100vw x the hero's 700 and fades to
   the page tone over the last 12% only: the wave's lit band sits at ~78% of
   the frame, so a longer fade (the frame's own 30% runs to a near-black the
   page does not have) greys it out. Anchored top so the band keeps its row. */
/* The WebP is the baseline; the AVIF pair only where typed image-set() parses,
   so a browser without it keeps the whole wave instead of an invalid layer. */
.dark [data-brand='signalmarkets'] .lobby__hero {
  --pm-hero-wave-image: url('./theme/hero-background.webp');
}

@media (max-width: 900px) {
  .dark [data-brand='signalmarkets'] .lobby__hero {
    --pm-hero-wave-image: url('./theme/hero-background-mobile.webp');
  }
}

@supports (background-image: image-set(url('x.avif') type('image/avif'))) {
  .dark [data-brand='signalmarkets'] .lobby__hero {
    --pm-hero-wave-image: image-set(
      url('./theme/hero-background.avif') type('image/avif'),
      url('./theme/hero-background.webp') type('image/webp')
    );
  }

  @media (max-width: 900px) {
    .dark [data-brand='signalmarkets'] .lobby__hero {
      --pm-hero-wave-image: image-set(
        url('./theme/hero-background-mobile.avif') type('image/avif'),
        url('./theme/hero-background-mobile.webp') type('image/webp')
      );
    }
  }
}

/* The brand paints the hero backdrop itself, so the CMS hook is switched off
   here: seeding never clears an existing hero row's circle media. */
[data-brand='signalmarkets'] .lobby__hero-mesh {
  display: none;
}

.dark [data-brand='signalmarkets'] .lobby__hero::before {
  content: '';
  position: absolute;
  z-index: 0;
  top: var(--pm-hero-wave-top, 0);
  left: var(--pm-hero-wave-left, calc(50% - 50vw));
  width: var(--pm-hero-wave-width, 100vw);
  /* Desktop stops where the next section starts: it is pulled up into the hero
     by `--am-section-margin-top` and, being unpositioned, paints UNDER this
     positioned box, so a full-height fade would veil the heading. */
  height: var(
    --pm-hero-wave-height,
    calc(100% + var(--am-section-margin-top, 0px))
  );
  /* Frame 2187:160166: #08171f ground, the 1440x700 image, then «Bottom fade»
     from transparent at 70% (490px) to the page #061115 at 700px. This box is
     shorter than 700 (see the height note above), so the fade is stated in
     pixels from the top and stops at the alpha the frame reaches at this box's
     bottom edge, instead of being squeezed into the shorter box.

     The art is top-anchored at 1440, as the frame. Wider, `cover` scales it up
     by width (height = 48.611vw); the offset keeps its bottom on the frame's
     700px line instead of cropping the bottom band away.

     Loading: a 40px blurred copy is inlined (--pm-hero-wave-placeholder), so
     the wave is on screen with the first paint and the full image lands on top
     of it. The full image is AVIF (12 KB) with a WebP fallback (21 KB), and a
     1080px pair on stacked layouts, in place of the 1.8 MB PNG. */
  --pm-hero-wave-placeholder: url('data:image/webp;base64,UklGRoYAAABXRUJQVlA4IHoAAABQBQCdASooABMAPtlYp0yoJSOiMBVdUQAbCWMAAI4VbQvzPssnq/WyA6h7A9pDroT9wOgA/vTSWHsH5ymqdTZ+w9DlOpGIBu/eiResN37KLrNs6ww3S4YOFW6jYZbkFWp6NYeA075GLJrWS6C4XjXLjSRFyyjSsAAAAA==');
  background:
    linear-gradient(
      to bottom,
      rgba(6, 17, 21, 0) 490px,
      var(--pm-hero-wave-fade-end, rgba(6, 17, 21, 0.82)) 662px
    ),
    var(--pm-hero-wave-image) center min(0px, calc(700px - 48.611vw)) / cover
      no-repeat,
    var(--pm-hero-wave-placeholder) center min(0px, calc(700px - 48.611vw)) /
      cover no-repeat,
    var(--pm-hero-wave-ground, #08171f);
  pointer-events: none;
}

/* The featured card is the slide widget's own `.pm-slide-card`, so its frame
   goes there. Putting it on the hero's column wrapper instead would draw a
   second border around the one the widget already paints. */
[data-brand='signalmarkets'] .lobby__hero .pm-slide-card {
  border-radius: 16px;
  /* Its own name, defaulting to `background/surface 2` — the PAGE tone, not
     the elevated one the shell's cards moved to. The frames give this card the
     same fill as the ground it sits on and separate it with the border and
     glow alone, so it reads as lit rather than raised.

     NOT --pm-card-bg, which is what the widget paints every `.pm-card` with
     and which means "a market card's fill". Two different statements that
     happen to land on one element, because the slide card carries `.pm-card`
     too — and a brand override of that shared name would not reach here
     anyway: the widget re-declares it on its own root, inside this one. */
  background: var(--pm-hero-card-bg);
  border: 1px solid var(--pm-hero-card-border, transparent);
  /* A green glow rather than a neutral drop — the card is the one lit surface
     in the section. 28.2px is the drawn value, kept rather than rounded. */
  box-shadow: 0 1px 28.2px 2px var(--pm-hero-card-glow, transparent);
}

/*
 * DARK. The accent and the primary CTA are both the brand's teal
 * `--accent-gradient` here — the same paint the header and Sign Up carry —
 * where light uses a green of its own. Reused rather than restated so the three
 * cannot drift apart.
 */
.dark [data-brand='signalmarkets'] .lobby__hero {
  --pm-hero-title-shadow: 0 2px 6px rgb(0 0 0 / 0.08);
  --pm-hero-accent-gradient: var(--accent-gradient);
  --pm-hero-subtitle-color: var(--pm-text-secondary-redraw); /* #d5dde3 */

  --pm-hero-action-primary-bg: var(--accent-gradient);
  --pm-hero-action-primary-text: var(--accent-cta-text); /* #05221b */
  --pm-hero-action-primary-shadow: 0 2px 6px 0 rgb(0 0 0 / 0.08);
  --pm-hero-action-secondary-bg: var(--color-SecondaryBg); /* #10232c */
  --pm-hero-action-secondary-text: var(
    --pm-text-secondary-redraw
  ); /* text/secondary */

  /* Featured box 2132:240156: the S5 surface on the S8 line, lifted by the
     green glow. One card for both slide variants. */
  --pm-hero-card-bg: var(--color-CardBg);
  --pm-hero-card-border: var(--color-PrimaryBorder);
  --pm-hero-card-glow: rgb(23 102 71 / 0.25);
}

/*
 * LIGHT. Its own green, and the CTA is a radial whose ellipse is far wider than
 * the pill (289 × 40 on a 140-wide button), so it reads as a top-lit wash; the
 * percentages keep that relationship at any label length. No shadow on either
 * the headline or the pill — that is the dark frame's alone.
 */
.light [data-brand='signalmarkets'] .lobby__hero {
  /* Starts at #139a66, not Figma's #35dd9c: that was ~1.7:1 on the white
     page, and this is ≥3.5:1 (large-text AA) at every stop. */
  --pm-hero-accent-gradient: linear-gradient(
    140deg,
    #139a66 13.12%,
    #0e7e52 60.74%
  );
  --pm-hero-subtitle-color: #666666; /* text/tertiary */

  --pm-hero-action-primary-bg: radial-gradient(
    206% 100% at 50% 0%,
    #20c082 0%,
    #169362 50%,
    #0b6542 100%
  );
  --pm-hero-action-secondary-bg: #f5f5f5; /* background/secondary */
  --pm-hero-action-secondary-text: #666666; /* text/tertiary */

  /* Light draws the card in the pale accent rather than a neutral grey — the
     border is `accent-green-tab-surface`, and the glow is the same colour at a
     quarter, so the card reads as lit from within rather than shadowed. */
  --pm-hero-card-border: #c8e9dc; /* accent-green-tab-surface */
  --pm-hero-card-glow: rgb(200 233 220 / 0.25);
}

/* ── how-it-works block (PRED-699) ───────────────────────────────────────────
   The FAQ in its `numbered` shape. Figma 1006:396266 (light) and 1006:429107
   (dark) — the two differ only in paint, so `faq-numbered.css` carries every
   measurement and this carries every colour.

   None of these land on a token this brand already has: the frames use their
   own greys (#111827 / #6b7280 in light, and `text/primary` / `text/secondary`
   in dark) where the shell's `--foreground` and `--muted-foreground` are
   #0d0d0d / #404040 and #ffffff / #bebebf. Close, and not the same. */
.light [data-brand='signalmarkets'] .faq-numbered {
  --faq-numbered-bg: #ffffff;
  --faq-numbered-border: #e6e6e6; /* border/default */
  --faq-numbered-divider: #e5e7eb;

  --faq-numbered-title-color: #111827;
  --faq-numbered-accent-color: #00a971; /* accent/green/bright */
  --faq-numbered-description-color: #6b7280;
  --faq-numbered-question-color: #111827;
  --faq-numbered-answer-color: #6b7280;
}

.dark [data-brand='signalmarkets'] .faq-numbered {
  --faq-numbered-bg: #1b2025; /* background/secondary */
  --faq-numbered-border: rgb(255 255 255 / 0.1); /* border/default */
  --faq-numbered-divider: #404040; /* border/input */

  --faq-numbered-title-color: #ffffff; /* text/primary */
  --faq-numbered-accent-color: #12d393; /* accent/green/bright */
  /* `text/secondary` — the name tokens.css gives #b3b3b3, which this brand's
     theme sheet declares on the same brand root and in the same document
     (theme.css: "both files land in the same document"). */
  --faq-numbered-description-color: var(--pm-text-secondary-redraw);
  --faq-numbered-question-color: #ffffff;
  --faq-numbered-answer-color: var(--pm-text-secondary-redraw);
}

/* The ghosted ordinal is the one paint the two frames share, so it is stated
   once rather than twice — #3b8b6e at 10% in both, which is also the component
   default; restated here only to keep the block readable as a whole. */
[data-brand='signalmarkets'] .faq-numbered {
  --faq-numbered-numeral-color: #3b8b6e;
}

/* ---- App loader (PRED-411) ----------------------------------------------
   Same mechanism as vsin: config `APP_LOADER.icon` holds the URL of a file
   uploaded to the Media Library. The SVG carries its own <style> and animates
   inside the <img>, so this only sizes it: the mark alone (the candlesticks,
   no wordmark), icon-sized. */
[data-brand='signalmarkets'] .pm-app-loader-icon {
  width: 72px;
  height: auto;
}

/* ---- Auth dialog (PRED-880) --------------------------------------------
   The card itself is widget-side (theme/components/auth.css); this is the
   antd dialog and the mobile sheet the shell hosts it in. */

/* antd puts the dialog 100px from the top; the frames centre it (333:39144 —
   a 518 card at y=191 of 900). Flex beats antd's inline-block + ::before. */
[data-brand='signalmarkets'] .pm-auth-modal-host .ant-modal-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
}

[data-brand='signalmarkets'] .pm-auth-modal-host .ant-modal {
  top: 0;
  margin: 0;
  padding-bottom: 0;
}

/* The dialog is 436 wide (host prop, shared across brands) and this card is
   400, so centre it in there rather than fight the inline width. */
[data-brand='signalmarkets'] .pm-auth-modal-host .ant-modal-container {
  display: flex;
  justify-content: center;
}

/* 40% black, blurred (BACKGROUND_BLUR 6), in both themes and on mobile. */
[data-brand='signalmarkets'] .pm-auth-modal-host .ant-modal-mask,
[data-brand='signalmarkets'] .form-sheet__overlay {
  background: rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

/* Dialog entrance: a short lift with a slight scale and a blur that clears,
   on a decelerating curve, instead of antd's zoom. The backdrop fades in a
   little slower, so the card lands on an already dimmed page. antd drives the
   phases with classes and waits for animationend, so the keyframes only
   replace its own. */
[data-brand='signalmarkets'] .ant-modal.ant-zoom-appear-prepare,
[data-brand='signalmarkets'] .ant-modal.ant-zoom-enter-prepare,
[data-brand='signalmarkets'] .ant-modal.ant-zoom-appear-start,
[data-brand='signalmarkets'] .ant-modal.ant-zoom-enter-start {
  opacity: 0;
  transform: translateY(14px) scale(0.96);
}

[data-brand='signalmarkets'] .ant-modal.ant-zoom-appear-active,
[data-brand='signalmarkets'] .ant-modal.ant-zoom-enter-active {
  animation: sm-dialog-in 460ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

[data-brand='signalmarkets'] .ant-modal.ant-zoom-leave-active {
  animation: sm-dialog-out 180ms cubic-bezier(0.4, 0, 1, 1) both;
}

[data-brand='signalmarkets'] .ant-modal-mask.ant-fade-appear-active,
[data-brand='signalmarkets'] .ant-modal-mask.ant-fade-enter-active {
  animation: sm-backdrop-in 320ms ease-out both;
}

[data-brand='signalmarkets'] .ant-modal-mask.ant-fade-leave-active {
  animation: sm-backdrop-out 200ms ease-in both;
}

@keyframes sm-dialog-in {
  from {
    opacity: 0;
    transform: translateY(14px) scale(0.96);
    filter: blur(6px);
  }
  60% {
    opacity: 1;
    filter: blur(0);
  }
  to {
    opacity: 1;
    transform: none;
    filter: none;
  }
}

@keyframes sm-dialog-out {
  from {
    opacity: 1;
    transform: none;
  }
  to {
    opacity: 0;
    transform: translateY(6px) scale(0.98);
  }
}

@keyframes sm-backdrop-in {
  from {
    opacity: 0;
    backdrop-filter: blur(0);
  }
  to {
    opacity: 1;
  }
}

@keyframes sm-backdrop-out {
  to {
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  [data-brand='signalmarkets'] .ant-modal.ant-zoom-appear-active,
  [data-brand='signalmarkets'] .ant-modal.ant-zoom-enter-active,
  [data-brand='signalmarkets'] .ant-modal.ant-zoom-leave-active {
    animation-duration: 1ms;
  }
}

/* ---- Onboarding canvas (PRED-919) ----------------------------------------
   The flow sits on its own page colour, a step off the app's, with a teal glow
   rising from the bottom and the brand's tile grid under it (575:267646 light,
   575:268751 dark). The onboarding route paints these three tokens; the cards,
   rail and CTA are widget-side, in theme/components/onboarding.css.

   The glow is a gradient rather than an export: Figma draws it as a 1382×992
   ellipse at 7% under a 500px blur, centred 47px above the bottom edge, which
   a radial gradient reproduces at any width for no bytes. The tiles are the
   one image — 504 of them, the frame's own geometry, 2.7 KB gzipped — shown
   the 199px the frame shows, the rest below the fold of the band. */
.dark [data-brand='signalmarkets'] {
  --pm-onboarding-canvas: #061115;
  --pm-onboarding-canvas-image:
    url('/brands/signalmarkets/theme/onboarding/canvas-tiles-dark.svg'),
    radial-gradient(
      941px 746px at 50% calc(100% - 47px),
      rgba(47, 191, 159, 0.072),
      rgba(47, 191, 159, 0.036) 73%,
      rgba(47, 191, 159, 0)
    );
}

.light [data-brand='signalmarkets'] {
  --pm-onboarding-canvas: #fbfbfb;
  --pm-onboarding-canvas-image:
    url('/brands/signalmarkets/theme/onboarding/canvas-tiles-light.svg'),
    radial-gradient(
      941px 746px at 50% calc(100% - 47px),
      rgba(1, 202, 160, 0.072),
      rgba(1, 202, 160, 0.036) 73%,
      rgba(1, 202, 160, 0)
    );
}

[data-brand='signalmarkets'] {
  --pm-onboarding-canvas-position: left 50% bottom -125px, center;
  --pm-onboarding-canvas-size: 1306px 324px, 100% 100%;

  /* The rail is the widest thing on the page (~781px at four steps) and the
     column is what it has to clear; 26px under the header, like the frame. */
  --pm-onboarding-max-width: 54rem;
  --pm-onboarding-page-padding: 1.25rem 1rem 2.5rem;
  --pm-onboarding-page-padding-lg: 1.625rem 1.5rem 4rem;
}

/* Mobile tab bar, active tab. The shell marks it with aria-current and paints
   it --foreground, but here the idle tabs are already --muted-foreground
   (#d5dde3), one step off #f2f5f7, so the active one did not read. The icons
   are images and cannot take a colour, so the tab gets the accent's soft pill
   (the radius and padding above were drawn for it) and an accent label. The
   live tab's own red is replaced too: Live here is the whole market list. */
[data-brand='signalmarkets'] .bottom-toolbar__item[aria-current='page'],
[data-brand='signalmarkets'] .bottom-toolbar__item[aria-current='page']:visited,
[data-brand='signalmarkets']
  .bottom-toolbar__item[data-id='live'][aria-current='page'],
[data-brand='signalmarkets']
  .bottom-toolbar__item[data-id='live'][aria-current='page']:visited {
  background: rgb(47 191 159 / 0.12);
  color: hsl(var(--primary));
}

[data-brand='signalmarkets']
  .bottom-toolbar__item[aria-current='page']
  .bottom-toolbar__label {
  color: hsl(var(--primary));
}
