/* Single-layer replacements for the removed Framer runtime.
   Everything visual the runtime used to do via JS lives here as plain CSS. */

/* --- hover states (measured from the hydrated site before removal). The runtime swapped
       the link for a "hover" variant: lavender pill, brand-purple label and caret, no
       underline. Inline styles carry the idle colours, hence !important. --- */
nav a[data-framer-name="btn-link"]:hover,
nav .framer-1w0jhyg:hover { background-color: rgba(225, 219, 255, 0.25) !important; transition: background-color .15s ease; }
nav a[data-framer-name="btn-link"]:hover p { --framer-text-color: rgb(68, 35, 252) !important;
  color: rgb(68, 35, 252) !important; text-decoration: none !important; }
/* The export writes a colour attribute onto the <g> and <path> as well as the <svg>, which
   blocks inheritance, so the descendants are named too -- production recolours all three. */
nav a[data-framer-name="btn-link"]:hover svg,
nav a[data-framer-name="btn-link"]:hover svg * { fill: rgb(68, 35, 252) !important; color: rgb(68, 35, 252) !important; }
/* The caret sits in its own container next to the label and dims with it. */
nav a[data-framer-name="btn-link"]:hover .framer-1w9zncl-container { opacity: 0.85 !important; }
/* Buy links. Production applies no filter to these -- brightness(1.25) was a stand-in and
   it fired on all 20 of them per page, including the outline buttons production leaves
   alone. Measured on live, the CTAs behave by component: the solid button swaps its fill
   and keeps its white label, the inline text links tint, and .framer-1ismqka is flat. */
.framer-v-1oss220.framer-2swqws:hover { background-color: rgb(123, 97, 255) !important;
  transition: background-color .15s ease; }
.framer-v-1oss220.framer-2swqws:hover .framer-text:not(a) { color: rgb(255, 255, 255) !important;
  border-color: rgb(255, 255, 255) !important; }
a.framer-styles-preset-13ft7jr:hover { color: rgb(202, 191, 255) !important;
  transition: color .15s ease; }
.framer-1axksfv a:hover { opacity: .5; transition: opacity .15s ease; }

/* --- avatar name tooltips: runtime kept these at opacity 0 until the avatar is hovered.
       !important because the export writes opacity:1 inline on every tooltip. The -56px
       is Framer's own .hover variant, which nothing adds the class for any more. --- */
.framer-DLwZG .framer-6eqqyq { opacity: 0 !important; pointer-events: none; transition: opacity .2s ease, top .2s ease; }
.framer-DLwZG:hover .framer-6eqqyq { opacity: 1 !important; top: -56px; }

/* --- back-to-top button. Three instances ship site-wide and production treats them
       differently: only the article one (.framer-pnnwkz, all 19 /learn/* pages) fades in
       on scroll and back out at the top. The /figma-plugins instance (.framer-160en92)
       and the icon-page one (.framer-yco38n, 704 pages) are visible from load, so they
       are left alone -- the export already ships them at opacity 1.
       Keyed by the instance class the same way the captured hover states are. --- */
[data-framer-name="Scrolltotop"].framer-pnnwkz { opacity: 0 !important; pointer-events: none; transition: opacity .3s ease; }
body.fx-scrolled [data-framer-name="Scrolltotop"].framer-pnnwkz { opacity: 1 !important; pointer-events: auto; }

/* --- mobile menu: production expands the header nav into this panel. Geometry and type
       below are measured off that expanded variant (390x638, 36px rows, 40px stride). --- */
.fx-overlay { position: fixed; inset: 0 0 auto 0; z-index: 9999; display: none;
  flex-direction: column; gap: 4px; padding: 12px 10px 16px; max-height: 100vh; overflow-y: auto;
  background: var(--token-f286fce1-72ae-4fb9-806e-5976843c8ef0, rgb(247, 247, 247));
  font-family: Inter, "Inter Placeholder", sans-serif; }
.fx-overlay.open { display: flex; }
.fx-overlay .fx-top { display: flex; justify-content: space-between; align-items: center; height: 36px; }
/* The panel covers the real header, so its logo and close button have to land exactly
   where the header's do or the logo appears to jump when the menu opens. Measured on live
   at 390px: logo at x=16 y=14 (28x32), close button at x=330 y=12 (48x36). */
.fx-overlay .fx-logo { display: flex; align-items: center; padding-left: 6px; }
.fx-close { width: 48px; height: 36px; margin-right: -2px; border: 0; border-radius: 12px; font-size: 15px; line-height: 1;
  cursor: pointer; color: var(--token-e2b224dd-83c7-4784-bd9a-7883298d3fc6, rgb(33, 33, 33));
  background: var(--token-b9f086d1-d6ed-4798-af48-c54d58fa58f8, rgb(224, 224, 224)); }
.fx-overlay nav { display: flex; flex-direction: column; gap: 4px;
  border-bottom: 1px solid var(--token-b9f086d1-d6ed-4798-af48-c54d58fa58f8, rgb(224, 224, 224)); }
.fx-overlay nav a { display: flex; align-items: center; height: 36px; padding-left: 12px; border-radius: 12px;
  font-size: 12px; font-weight: 700; text-decoration: underline; text-underline-offset: 3px;
  color: var(--token-e2b224dd-83c7-4784-bd9a-7883298d3fc6, rgb(33, 33, 33)); }
.fx-overlay nav a:hover { background: rgba(225, 219, 255, 0.25); }
.fx-overlay .fx-extra { display: flex; flex-direction: column; gap: 3px; margin-top: 16px; }
/* text-decoration: none is set, not merely omitted -- the export underlines anchors, and a
   line on this anchor is drawn through every descendant and cannot be taken back further
   down, so text-decoration: none on the glyph span did nothing and ↓ and 𓏗𓏗 came out
   underlined. The label below carries the underline instead. */
.fx-overlay .fx-extra > a { display: flex; align-items: center; justify-content: center; gap: 7px;
  height: 36px; border-radius: 12px; font-size: 12px; font-weight: 700;
  text-decoration: none;
  color: var(--token-e2b224dd-83c7-4784-bd9a-7883298d3fc6, rgb(33, 33, 33)); }
.fx-overlay .fx-extra > a > span { font-size: 16px; }
/* These rows are flex containers, so a bare text node becomes an anonymous flex item that
   no rule can reach -- hence the label is a span with the underline on it. */
.fx-overlay .fx-extra > a .fx-lbl { font-size: 12px; text-decoration: underline;
  text-underline-offset: 3px; }
.fx-cta { background: rgb(68, 35, 252); color: #fff !important; font-size: 14px !important;
  text-decoration: none !important; }
.fx-cta:hover { background: #7b61ff; }

/* --- desktop nav dropdowns: geometry and type measured off the hydrated panels --- */
/* Fades rather than display:none/block. Two panels could also be on screen at once,
   because each closed on its own 180ms timer -- moving from Product to Resources showed
   both. site.js now closes the previous one the moment another opens.
   Production swaps instantly with no fade; the 140ms is a deliberate softening, asked for. */
.fx-dd { position: absolute; z-index: 9998; border-radius: 10px; overflow: hidden;
  /* Pinned to 0,0 until the first open. Without a top/left an absolutely positioned box
     falls back to its static position -- the end of <body> -- and since these are no longer
     display:none, all three panels extended every page by 251px. */
  top: 0; left: 0;
  background: rgb(250, 255, 255); font-family: Inter, "Inter Placeholder", sans-serif;
  opacity: 0; visibility: hidden; transform: translateY(-4px);
  transition: opacity .14s ease, transform .14s ease, visibility 0s linear .14s; }
.fx-dd.open { opacity: 1; visibility: visible; transform: translateY(0);
  transition: opacity .14s ease, transform .14s ease, visibility 0s; }
/* Live outlines the panel with a pseudo-element rather than a border on the box itself:
   .framer-1f4sfha::after { content:""; inset:0; border:1px solid rgb(224,224,224) }.
   Mine had no ::after, so the panel floated with no edge. There is no shadow on live --
   box-shadow computes to none on the panel and on its wrapper. */
.fx-dd::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  border: 1px solid rgb(224, 224, 224); border-radius: inherit; }
.fx-dd a { display: flex; align-items: center; gap: 7px; height: 42px; padding: 0 12px;
  text-decoration: none; color: var(--token-51805f0a-b892-433f-b5a4-99aa98b6239a, rgb(74, 74, 74)); }
/* 62.67, not 63: live's rows are fractional, so four of them plus three 1px gaps come to
   253 rather than 255. Rounding them up left the panel 2px taller than live's. */
.fx-dd-wide a { align-items: flex-start; height: 62.67px; padding: 8px 12px; }
/* gap, not a margin on each row: live spaces these with gap:1px and its rows are a
   fractional 63.x tall, so margins round the panel 2px taller than live's. */
.fx-dd-wide { display: flex; flex-direction: column; gap: 1px; }
.fx-dd a:hover { background: rgba(225, 219, 255, 0.25); }
.fx-dd .fx-dd-i { flex: none; width: 16px; height: 16px; display: flex; align-items: center; }
.fx-dd:not(.fx-dd-wide) .fx-dd-i { order: 2; }   /* the ↗ trails the label on the plain lists */
/* The row is align-items:flex-start so the two-line text block sits under the 8px top
   padding; the 16px icon inherited that and hung 15px above the text's middle. It centres
   against the whole title+description block. For centring on the title line instead,
   swap this for margin-top: 3px -- half the 22px title line minus half the icon. */
.fx-dd-wide .fx-dd-i { align-self: center; }
.fx-dd .fx-dd-i:empty { display: none; }
.fx-dd .fx-dd-t { display: flex; flex-direction: column; justify-content: center; min-width: 0; }
/* Live leaves 4px between the row's title and its description; without it the pair sat
   flush and every description line rendered 4px high. The row height is fixed, so the
   panel height does not move: 22 + 4 + 20 + 16 padding = the 62.67px row. */
.fx-dd-wide .fx-dd-t { gap: 4px; }
.fx-dd b { font-size: 13px; font-weight: 700; line-height: 22px; text-decoration: underline; text-underline-offset: 3px; }
/* The two panel shapes are typed differently on live: the plain lists are 14px and
   capitalised by CSS -- which is why the row data reads "Affiliate program" and renders
   "Affiliate Program" -- while the wide, two-line rows stay 13px with no transform. */
.fx-dd:not(.fx-dd-wide) b { font-size: 14px; text-transform: capitalize; }
.fx-dd small { font-size: 12px; font-weight: 500; line-height: 20px; text-decoration: none; }

/* --- code blocks: the copy button on /code-components tints on hover. It is a different
       instance of the copy component from the icon-page one (.framer-18epsuv-container),
       which production leaves flat, so the rule names this container. The button carries no
       class of its own.
       Keyed on the block wrapper rather than the button: the code text is painted over the
       button, so the button never receives :hover and a rule on it could never fire. The
       wrapper is exactly button-sized (585x64), so this is not the container over-reach the
       rules further down were guilty of. --- */
.framer-130w71a:hover .framer-1ewra6z-container button { background-color: rgba(255, 255, 255, 0.06) !important;
  transition: background-color .15s ease; }

/* --- icon tiles: the "Copied!" label ships at opacity 1 and was faded in by the runtime.
       The size is pinned because on a symbol tile the label inherits the glyph's type -- it
       came out at 28px/400 against the icon tiles' 16px/500. Live uses 16px/500 for both. --- */
button[aria-live] span[aria-hidden="true"] { font-size: 16px !important; font-weight: 500 !important;
  line-height: 16px !important; }
button[aria-live] > span > span[aria-hidden="true"] { opacity: 0 !important; transition: opacity .2s ease; }
/* The three install-snippet tiles (/code-components and home) were exported with their
   "Copied!" label stuck at inline opacity:1, so the tile read "Copied! npm i ..." at rest.
   Hidden here for every copy button -- icon tiles already rest at 0 -- and revealed again
   by the .fx-copied rule below, which must stay after this one to win the tie. The install
   tiles never get .fx-copied: their feedback is the command text itself (see site.js). */
button[aria-live] > span > span[aria-hidden="true"] { opacity: 0 !important; }
button[aria-live].fx-copied > span > span[aria-hidden="true"] { opacity: 1 !important; }

/* --- marquees (replaces the Ticker component's JS-driven translateX) --- */
@keyframes fx-ticker-l { from { transform: translateX(0) } to { transform: translateX(calc(var(--fx-shift) * -1)) } }
@keyframes fx-ticker-r { from { transform: translateX(calc(var(--fx-shift) * -1)) } to { transform: translateX(0) } }

/* Four hover rules that set `transform` on parallax layers were removed here:
     .framer-u8i48r:hover, .framer-nwsm3e:hover .framer-14rkj72,
     .framer-1hsjp5r:hover .framer-4bgioe, .framer-4bgioe:hover
   Each snapped its layer to a fixed -0.5/-1.5px, and being !important it beat the inline
   transform site.js drives on scroll, so pointing at a section yanked its screenshot layer
   out of position. Production does not move on hover -- verified with the pointer parked
   away and then moved on, without scrolling in between.
   They are capture artifacts: hover-capture sampled these while the parallax was mid-flight
   and read the difference as a hover effect. Its guard only drops deltas over 8px. */

/* ==== captured hover states =========================================================
   Framer's hover variants lived in the deleted JS modules, not in the exported CSS: the
   runtime rewrote inline styles on mouseenter. hover-capture.mjs hovers every component
   on production, diffs the computed styles, and emits the deltas below, keyed by the
   component's own framer-* class so one rule covers every instance site-wide.
   Regenerate:  node hover-capture.mjs --out f.json && node hover-capture.mjs --emit f.json --freq <pages>
   ================================================================================== */
.framer-2swqws:hover { background-color: rgba(225, 219, 255, 0.25) !important }
/* .framer-f3rjmz (the <nav> itself), .framer-icee14 and .framer-l30pnt were dropped from
   the rules below for the same reason as .framer-jeTq7 above: they are containers, so the
   rule fired for everything under them the moment the pointer entered. Verified against
   live -- production is flat on all three. Dropping .framer-icee14 also stops the 22 FAQ
   rows (.framer-94zwut) and .framer-dkageb reacting, which they inherited from it. */
.framer-2swqws:hover .framer-oazdk7 { opacity: 0.85 !important }
.framer-3ppakv:hover .framer-text:not(a),
.framer-2swqws:hover .framer-text:not(a),
.framer-fi3wqr:hover .framer-text:not(a),
.framer-aItCH:hover .framer-text:not(a),
.framer-gkfav2:hover .framer-text:not(a),
.framer-9wcjkg:hover .framer-text:not(a),
.framer-8wdq60:hover .framer-text:not(a),
.framer-x3yc2c:hover .framer-text:not(a),
.framer-nh0msd:hover .framer-text:not(a),
.framer-18hpz38:hover .framer-text:not(a),
.framer-q1258x:hover .framer-text:not(a),
.framer-1wpqd7j:hover .framer-text:not(a) { border-color: rgb(68, 35, 252) !important; color: rgb(68, 35, 252) !important; text-decoration-line: none !important }
.framer-v-22ebfc:hover { background-color: rgb(242, 255, 246) !important }
.framer-v-22ebfc:hover .framer-text:not(a) { border-color: rgb(69, 166, 0) !important; color: rgb(69, 166, 0) !important }
.framer-v-1rpsh59:hover { background-color: rgb(51, 51, 51) !important }
.framer-of26k:hover,
.framer-14vvuw4:hover,
.framer-lk7qlc:hover,
.framer-17cumvg:hover { opacity: 0.9 !important }
.framer-4heyxi:hover .framer-zts0ep,
.framer-r5ystn:hover .framer-zts0ep,
.framer-1mt7cd3:hover .framer-zts0ep { opacity: 0.6 !important }
.framer-1wg3pgn:hover .framer-text:not(a),
.framer-3i43o5:hover .framer-text:not(a),
.framer-dkdvA:hover .framer-text:not(a),
.framer-1dar6a8:hover .framer-text:not(a) { border-color: rgb(120, 120, 120) !important; color: rgb(120, 120, 120) !important }
/* The Mono / Root UI Kit cards. Their end states were already right, which is why every
   screenshot matched -- what was missing is the motion, and that a still frame cannot show.
   Two separate faults: the dim was applied with no transition, so it snapped; and live
   triggers it from the whole card while these rules only fired when the pointer was over
   the link itself, so the Root card did nothing at all from most of its surface.
   Measured on live: Mono dims to 0.95 settling in ~141ms, Root to 0.8 in ~167ms. */
.framer-iroqxo { transition: opacity .2s ease; }
.framer-6b1mnq { transition: opacity .19s ease; }
.framer-iroqxo:hover,
.framer-kactj3:hover .framer-iroqxo { opacity: 0.95 !important }
.framer-z248sa:hover .framer-6b1mnq { opacity: 0.8 !important }
/* Restored after a better-instrumented check: production does apply this one from the
   section. The earlier removal was based on a reading taken before hoverdiff keyed its
   snapshot rows by element signature, which mis-paired nodes on large containers. */
.framer-1w1kc9f:hover .framer-a5ch6l { opacity: 0.8 !important; transform: translate(2px, 0px) !important }
.framer-6b1mnq:hover { opacity: 0.8 !important }
/* .framer-jeTq7 and .framer-KflZv were dropped from the three rules below: hover-capture
   keys a rule on the sampled element's rarest class, and for these it sampled a section
   wrapper rather than the component. .framer-jeTq7 wraps up to 1356 .framer-text nodes and
   .framer-KflZv 444, so pointing anywhere inside a section repainted the whole section --
   production does nothing there. The sibling classes are the real components and still
   carry the effect. Guard:  node hoverscope.mjs  */
/* .framer-1lqov8q is a whole FAQ item: question row (.framer-yymlk4) plus answer body
   (.framer-94zwut). Production darkens only the question when you point at the item, so the
   descendant half is scoped to the question row -- unscoped it also blackened every answer
   paragraph underneath. */
.framer-1lqov8q:hover .framer-yymlk4 .framer-text:not(a),
.framer-yymlk4:hover .framer-text:not(a),
.framer-1m0l7h4:hover .framer-text:not(a) { border-color: rgb(0, 0, 0) !important; color: rgb(0, 0, 0) !important }
.framer-1lqov8q:hover .framer-11oaxt3 { background-color: rgb(247, 247, 247) !important; border-radius: 12px !important }
.framer-ntju01:hover,
.framer-cn032v:hover,
.framer-14r3ba8:hover,
.framer-17g8us5:hover,
.framer-15jqwfz:hover { opacity: 0.6 !important }
/* .framer-ntju01 also appears in the 0.6 rule above. Two captures of one component
   disagreed and the later rule won, so every instance got 0.6. Live only agrees for
   .framer-1pd3v4b; the two footer link shapes each do something else, and since the
   anchor's opacity compounds with its inner text's own 0.5 the difference is visible:
     .framer-1gkfey6  anchor fades to 0.5 -> 0.5 x 0.5
     .framer-gfz15w   anchor does not fade at all, only the inner .framer-c09ivb does */
.framer-1gkfey6.framer-ntju01:hover { opacity: 0.5 !important }
.framer-gfz15w.framer-ntju01:hover { opacity: 1 !important }
.framer-y9hpqs:hover,
.framer-c09ivb:hover,
.framer-15yppw0:hover,
.framer-1l9elo2:hover,
.framer-1hu61k:hover,
.framer-1kvy0ir:hover,
.framer-1lceqqs:hover,
.framer-cg4yo7:hover { opacity: 0.5 !important }
.framer-1gkfey6:hover .framer-y9hpqs { opacity: 0.5 !important }
.framer-gfz15w:hover .framer-c09ivb { opacity: 0.5 !important }
.framer-jrm1ky:hover .framer-15yppw0,
.framer-5mhj8j:hover .framer-15yppw0 { opacity: 0.5 !important }
.framer-axz5fk:hover .framer-1l9elo2 { opacity: 0.5 !important }
.framer-1fa530l:hover .framer-1hu61k { opacity: 0.5 !important }
.framer-1nbxte0:hover .framer-1kvy0ir { opacity: 0.5 !important }
.framer-2boh4i:hover .framer-1lceqqs { opacity: 0.5 !important }
.framer-1hfeswu:hover .framer-cg4yo7 { opacity: 0.5 !important }
/* The .framer-1cebgfe variant of .framer-19370ml is the one instance live does not fade;
   its .framer-qz1ycm siblings on the same page do. Removing the whole rule broke those
   three, so the exception is written as an exception. */
.framer-ugjpkh:hover,
.framer-19370ml:hover:not(.framer-1cebgfe) { opacity: 0.7 !important }
.framer-3ppakv:hover .framer-1yikym5,
.framer-aItCH:hover .framer-1yikym5 { opacity: 1 !important }
.framer-3ppakv:hover .framer-1leek9q,
.framer-aItCH:hover .framer-1leek9q { opacity: 0.6 !important }
.framer-7cCRE:hover { background-color: rgb(224, 224, 224) !important }
.framer-1uff1g7:hover { opacity: 0.7 !important; transform: translate(0px, -3px) !important }
.framer-1duo6cc:hover .framer-1xk56n9,
.framer-5L0Xh:hover .framer-1xk56n9 { background-color: rgb(247, 247, 247) !important }
.framer-fwNFg:hover .framer-1yw035e { opacity: 0.6 !important }
.framer-fwNFg:hover .framer-aq8lzh { opacity: 1 !important }
.framer-1uhz5if:hover .framer-1y49kib { opacity: 0.8 !important; transform: translate(2px, 0px) !important }
.framer-h54zc:hover .framer-text:not(a),
.framer-12hlu4d-container:hover .framer-text:not(a),
.framer-rttgvl-container:hover .framer-text:not(a),
.framer-g4sqp8-container:hover .framer-text:not(a),
.framer-377te9-container:hover .framer-text:not(a),
.framer-15z9wuz-container:hover .framer-text:not(a),
.framer-1err5jp-container:hover .framer-text:not(a),
.framer-p3afas-container:hover .framer-text:not(a),
.framer-zaj1lp-container:hover .framer-text:not(a),
.framer-12kmgg0-container:hover .framer-text:not(a),
.framer-6s7i13-container:hover .framer-text:not(a),
.framer-13h2oqq-container:hover .framer-text:not(a),
.framer-1tmlg55-container:hover .framer-text:not(a),
.framer-1di2ux8-container:hover .framer-text:not(a),
.framer-yhcjo5-container:hover .framer-text:not(a),
/* .framer-yo0hsy and .framer-1k76py4 dropped from these three rules: they are the icon
   page's two sidebar columns (220x740 and 220x688, 32 and 30 text nodes), so the rules
   turned the whole sidebar purple whenever the pointer entered it, on all 704 icon pages.
   Production is flat on both. The -container siblings are the real row components. */
.framer-2x3sul-container:hover .framer-text:not(a) { border-color: rgb(68, 35, 252) !important; color: rgb(68, 35, 252) !important }
.framer-h54zc:hover .framer-xc0iqk { opacity: 1 !important }
.framer-1am4yoa:hover .framer-90853w { filter: grayscale(0) !important }
.framer-1am4yoa:hover .framer-6eqqyq { border-radius: 10.0616% / 21.6704% !important; opacity: 1 !important; top: -56px !important }
.framer-90853w:hover,
.framer-11qx47m:hover { filter: grayscale(0) !important }
.framer-sgs5sv:hover .framer-1pd3v4b.framer-ntju01 { opacity: 0.6 !important }
.framer-5mhj8j:hover .framer-jrm1ky.framer-ntju01 { opacity: 0.5 !important }
.framer-1vgxdh6:hover .framer-text:not(a) { border-color: rgba(74, 74, 74, 0.5) !important; color: rgba(74, 74, 74, 0.5) !important; text-decoration-line: none !important }
.framer-13rbrsj:hover .framer-text:not(a) { border-color: rgba(74, 74, 74, 0.5) !important; color: rgba(74, 74, 74, 0.5) !important }
.framer-kactj3:hover .framer-iroqxo.framer-k2nqwt { opacity: 0.95 !important }
.framer-z248sa:hover .framer-6b1mnq.framer-k2nqwt { opacity: 0.8 !important }
.framer-tqke8l:hover .framer-11qx47m,
.framer-1nb526y:hover .framer-11qx47m,
.framer-ohql8g:hover .framer-11qx47m { filter: grayscale(0) !important }
.framer-1vt85rp:hover .framer-1mbkkin.framer-ugjpkh,
.framer-1keaohu:hover .framer-1mbkkin.framer-ugjpkh { opacity: 0.7 !important }
.framer-1yikym5:hover,
.framer-123scbh:hover,
.framer-aq8lzh:hover { opacity: 1 !important }
.framer-qJP0X:hover { background-color: rgb(255, 255, 255) !important }
.framer-qJP0X:hover .framer-text:not(a) { border-color: rgb(51, 51, 51) !important; color: rgb(51, 51, 51) !important }
.framer-qJP0X:hover .framer-123scbh { opacity: 1 !important }
.framer-1jlwfcd:hover .framer-lk7qlc.framer-18b291e,
.framer-7m0fav:hover .framer-lk7qlc.framer-18b291e { opacity: 0.9 !important }
.framer-1m0l7h4:hover .framer-11oaxt3 { background-color: rgb(247, 247, 247) !important; border-radius: 9px !important }
.framer-xc0iqk:hover { opacity: 1 !important }
.framer-1sem11w:hover .framer-15mgkbl.framer-19370ml,
.framer-klru80:hover .framer-15mgkbl.framer-19370ml { opacity: 0.7 !important }
.framer-1w9zncl-container:hover { opacity: 0.85 !important }
.framer-r5ystn:hover .framer-1ep1fjv-container,
.framer-1mt7cd3:hover .framer-1ep1fjv-container,
.framer-4heyxi:hover .framer-1ep1fjv-container { opacity: 0.6 !important }
.framer-1lqov8q:hover .framer-89lh4u-container,
.framer-1m0l7h4:hover .framer-89lh4u-container { opacity: 1 !important }
.framer-eqnv8o:hover .framer-1jmqw1v-container { opacity: 0.8 !important }
.framer-8svdba:hover .framer-175hqe6-container { opacity: 0.9 !important }
.framer-6kn7r1:hover .framer-qvn00-container { opacity: 0.9 !important }
.framer-12hlu4d-container:hover .framer-xc0iqk,
.framer-rttgvl-container:hover .framer-xc0iqk,
.framer-g4sqp8-container:hover .framer-xc0iqk,
.framer-377te9-container:hover .framer-xc0iqk,
.framer-15z9wuz-container:hover .framer-xc0iqk,
.framer-1err5jp-container:hover .framer-xc0iqk,
.framer-p3afas-container:hover .framer-xc0iqk,
.framer-zaj1lp-container:hover .framer-xc0iqk,
.framer-12kmgg0-container:hover .framer-xc0iqk,
.framer-6s7i13-container:hover .framer-xc0iqk,
.framer-13h2oqq-container:hover .framer-xc0iqk,
.framer-1tmlg55-container:hover .framer-xc0iqk,
.framer-1di2ux8-container:hover .framer-xc0iqk,
.framer-yhcjo5-container:hover .framer-xc0iqk,
.framer-2x3sul-container:hover .framer-xc0iqk { opacity: 1 !important }
.framer-z0til9:hover .framer-1ajwedi-container,
.framer-1slsdch:hover .framer-1ajwedi-container { opacity: 0.9 !important }
.framer-1w9zncl-container:hover svg { border-color: rgb(68, 35, 252) !important; color: rgb(68, 35, 252) !important; fill: rgb(68, 35, 252) !important }
.framer-3i43o5:hover svg,
.framer-1xvyf05-container:hover svg { border-color: rgb(120, 120, 120) !important; color: rgb(120, 120, 120) !important; fill: rgb(120, 120, 120) !important }

/* ==== card outlines on hover ========================================================
   Framer draws these outlines with --border-* custom properties on a data-border element,
   not with a real CSS border. The original hover capture only sampled standard properties,
   so every one of these was lost: the cards changed their label colour on hover and nothing
   else. Captured off live: #4423fc, 3px, solid.
   Keyed on the border element itself and on an enclosing link, because the same card is
   reached both ways depending on the page.
   ================================================================================== */
.framer-1w5jsnm:hover,
.framer-1of4987:hover,
.framer-1of4987:hover .framer-1w5jsnm,
a:hover > .framer-1w5jsnm,
a:hover .framer-1of4987,
a:hover .framer-1w5jsnm {
  --border-color: #4423fc !important;
  --border-style: solid !important;
  --border-top-width: 3px !important;
  --border-right-width: 3px !important;
  --border-bottom-width: 3px !important;
  --border-left-width: 3px !important;
}

/* --- component-card reveals: production keeps these hidden until hover, the export ships
       them at full opacity so the hover effect was showing in the resting state. The hover
       rules further up already set 1 and 0.6; only the idle state was missing. --- */
.framer-3ppakv .framer-1yikym5, .framer-aItCH .framer-1yikym5,
.framer-3ppakv .framer-1leek9q, .framer-aItCH .framer-1leek9q {
  opacity: 0 !important; transition: opacity .2s ease;
}
/* Two more sets of the same thing, missed the first time round. The export writes
   opacity:1 inline on every element the runtime keeps at 0, and only the pairs above had
   their idle state restored. Found by diffing computed opacity per class against live:
     /code-components  .framer-1yw035e  the gradient wash across the card foot
                       .framer-aq8lzh   the trailing arrow next to the card's label
     /copy-paste-icons .framer-xc0iqk   the arrow after "All Icons" in the sidebar rows,
                                        on the hub and all 704 icon pages
   The hover rules for all three already exist further up; only idle was wrong, so each
   showed its hover state permanently -- and .framer-1yw035e read as a dim-on-hover. */
.framer-fwNFg .framer-1yw035e,
.framer-fwNFg .framer-aq8lzh,
.framer-h54zc .framer-xc0iqk {
  opacity: 0 !important; transition: opacity .2s ease;
}

/* --- husks of the removed copy-paste-symbols section: the CMS collection behind both is
       empty, so production's runtime renders nothing for them and the export ships the
       empty containers. They hold no text on any page -- checked across the standalone
       pages and all four layouts -- and they are laid out with `gap`, so display:none
       takes their spacing with them and lands on live's box exactly.

         .framer-13dfucy  a blank 36px row in the "Helpful resources" sidebar, sitting
                          between "Copy and paste icons" and "Blog posts and updates" on
                          /affiliate /faq /figma-shortcuts /legal /terms. Visible as a
                          46px hole at >=1200px; the sidebar is hidden below that.
         .framer-13fzu80  the footer's "Symbols" column, six empty title rows, ~160px of
                          dead space on all 871 pages. Shorter than the tallest footer
                          column, so it never moved the page height.

       Hidden rather than deleted from the markup: the block is a constant inside the
       cluster layouts and inside the icon cluster's chunk dictionary, which
       tools/replace.mjs does not reach, so a partial delete would leave the 704 icon
       pages inconsistent -- and re-cutting tools/baseline-hashes.json to accept the new
       bytes would spend the byte-for-byte guarantee on an invisible container. Say the
       word if you want the bytes gone as well; it is a dictionary-aware pass. --- */
.framer-13dfucy,
.framer-13fzu80 { display: none !important }

/* --- consent banner. Built by site.js, so it exists only on pages that load gtag and
       only for visitors the EEA/UK rule covers. Bottom sheet rather than a full-screen
       interstitial: an overlay on first paint would be its own Core Web Vitals problem,
       and Google treats intrusive interstitials as a mobile ranking signal. Fixed, so it
       never shifts layout -- CLS stays where it was. --- */
.fx-consent {
  position: fixed; inset: auto 0 0 0; z-index: 10000;
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px;
  padding: 14px 20px;
  background: var(--token-f286fce1-72ae-4fb9-806e-5976843c8ef0, rgb(247, 247, 247));
  border-top: 1px solid var(--token-b9f086d1-d6ed-4798-af48-c54d58fa58f8, rgb(224, 224, 224));
  font-family: Inter, "Inter Placeholder", sans-serif;
  color: var(--token-51805f0a-b892-433f-b5a4-99aa98b6239a, rgb(74, 74, 74));
}
.fx-consent p { flex: 1 1 320px; margin: 0; font-size: 13px; line-height: 20px; }
.fx-consent-actions { display: flex; align-items: center; gap: 10px; margin-left: auto; }
.fx-consent-actions a {
  font-size: 13px; text-decoration: underline; text-underline-offset: 3px;
  color: var(--token-51805f0a-b892-433f-b5a4-99aa98b6239a, rgb(74, 74, 74));
}
.fx-consent button {
  border: 0; border-radius: 10px; padding: 9px 16px; cursor: pointer;
  font-family: inherit; font-size: 13px; font-weight: 700; line-height: 1;
}
/* Decline is as reachable as Accept -- an equally weighted pair is what the EDPB's
   deceptive-design guidance asks for, and a buried decline is the thing regulators fine. */
.fx-consent-no {
  background: var(--token-b9f086d1-d6ed-4798-af48-c54d58fa58f8, rgb(224, 224, 224));
  color: var(--token-e2b224dd-83c7-4784-bd9a-7883298d3fc6, rgb(33, 33, 33));
}
.fx-consent-yes { background: rgb(68, 35, 252); color: #fff; }
.fx-consent-no:hover { background: rgb(212, 212, 212); }
.fx-consent-yes:hover { background: rgb(123, 97, 255); }
@media (max-width: 719.98px) {
  .fx-consent { padding: 14px 16px; }
  .fx-consent-actions { width: 100%; margin-left: 0; }
  .fx-consent-actions button { flex: 1; }
}

/* --- rating row on the testimonial cards. The page marks each testimonial up as a 5/5
       Review, and Google's review-snippet policy wants a rating the structured data
       claims to be visible on the page -- only the featured Nick St. Pierre quote showed
       stars. Same orange token and weight as that block, sized to the card's 15.5px body
       rather than its 19px display size; change the one font-size to rebalance.
       role="img" + aria-label so a screen reader says "Rated 5 out of 5" instead of
       spelling out five glyphs. --- */
.fx-stars {
  font-family: Inter, "Inter Placeholder", sans-serif;
  font-size: 15px;
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: -0.0125em;
  color: var(--token-c2aaa1db-b5b5-457f-9bf3-3e00ee894d05, rgb(250, 107, 17));
}

/* The quote carries a 20px top margin from its text preset, which was spacing it off the
   top of the box when nothing sat above it. With the rating row there it reads as a gap
   between the two, so 4px. Scoped to the rating row's sibling: the same preset sets that
   margin on paragraphs all over the site. */
.fx-stars + p { margin-top: 4px !important; }

/* --- text inputs: production thickens the inner ring from 0 to 3px on focus. Measured on
       live; hover is deliberately flat there, so only :focus is styled. Without this the
       field gave no feedback at all when you clicked into it. --- */
.framer-custom-input:focus,
.framer-custom-input:focus-visible {
  box-shadow: rgb(164, 145, 255) 0 0 0 3px inset,
              rgba(0, 0, 0, 0.12) 0 1px 2px 0,
              rgb(224, 224, 224) 0 0 0 1px inset !important;
  outline: none;
}

/* --- what you type in a field: the export paints every input with the placeholder grey
       inline (rgb(150, 150, 150)), so an address you have just typed is as faint as the
       prompt it replaced. The placeholder keeps that grey -- it is drawn by a separate
       ::placeholder rule off --framer-custom-placeholder-color, and that rule is
       !important, so darkening the field text cannot reach it. --- */
.framer-custom-input,
input[type="text"],
input[type="email"],
textarea {
  color: #1a1a1a !important;
}

/* --- a submit button that has done its job. The export paints the button inline -- black
       fill, white label, cursor:pointer -- and has no disabled state at all, so a confirmed
       signup still looked and felt clickable. Greys taken from the same tokens the rest of
       the site uses for inactive surfaces and muted text. Inline styles, hence !important. --- */
input[type="submit"]:disabled,
button:disabled {
  background: var(--token-b9f086d1-d6ed-4798-af48-c54d58fa58f8, rgb(224, 224, 224)) !important;
  color: var(--token-79601831-093c-4185-8349-a6eeb5ff204f, rgb(150, 150, 150)) !important;
  box-shadow: none !important;
  cursor: default !important;
  pointer-events: none;   /* no hover state to reach, and nothing to click */
}

/* --- icon tiles: the glyph steps aside while "Copied!" shows, as it does on live. Without
       this the label sat on top of the icon. --- */
.fx-icon-art { transition: opacity .15s ease; }
.fx-icon-art.fx-hidden { opacity: 0 !important; }

/* --- primary button ("→ Own Frames X UI Kits ←"): live keeps --border-color #6950fa set at
       rest with the widths at 0, and brings the widths to 1px on hover, so the outline
       appears rather than changing colour. Pressed is the same as hover on live -- the only
       thing that changes on mousedown is the browser's own active link colour. --- */
.framer-1ismqka:hover {
  /* The fill is a radial gradient, not a background-color -- which is why sampling
     background-color showed no hover change while more than half the button's pixels were
     wrong. Live grows the gradient from 50% to 60% and lightens both stops. Pressed is
     identical to hover. */
  background-image: radial-gradient(60% 60% at 50% 100%,
    rgb(164, 145, 255) 0%, rgb(105, 80, 250) 100%) !important;
  transition: background-image .2s ease;
  --border-top-width: 1px !important;
  --border-right-width: 1px !important;
  --border-bottom-width: 1px !important;
  --border-left-width: 1px !important;
  --border-style: solid !important;
}

