/* V4 commerce presentation. Checkout authority stays in upgrade.js. */
.wrap:has(.pricing){max-width:1240px}
.pricing{max-width:1120px}
.pricing-intro{margin:0 0 28px;text-align:center;color:var(--text-secondary)}
/* Three cards — Pro Monthly, Pro Yearly (highlighted), Pro Lifetime (plan 4.0, owner
   2026-11-07: no Free card, no Monthly/Yearly switch). Every card is the same
   column skeleton (name + tagline, price, terms, list, CTA) so headers, prices,
   lists and the bottom-aligned CTAs line up across the row. */
.plan-grid{grid-template-columns:repeat(3,minmax(0,1fr));align-items:stretch;gap:20px}
.plan{padding:30px 24px 26px;min-width:0}
.plan-head{display:flex;flex-direction:column;gap:10px;min-height:72px;margin-bottom:6px}
.plan-name{font-size:13px;letter-spacing:.05em}
.plan-tagline{margin:0;font-size:13px;line-height:1.5;color:var(--text-muted)}
.plan-price{font-size:clamp(27px,3vw,38px);overflow-wrap:anywhere;line-height:1.25;min-height:58px;margin-top:4px}
.plan-sub{min-height:44px;font-size:13px;margin-bottom:16px;line-height:1.55}
.plan .feat{flex:1;margin-bottom:24px;font-size:13px;line-height:1.6}
.plan .feat li{padding-left:20px;margin-bottom:12px}
/* One CTA family: same height, width, radius, type and icon slot. Free is the
   quiet (outlined) member of the family, never a different kind of control. */
.plan-cta{width:100%;height:48px;min-height:48px;line-height:1.2;padding:12px 20px;font-size:14px;text-align:center;text-decoration:none;border-radius:12px}
.plan-cta.secondary{background:rgba(255,255,255,.03);color:var(--text);border:1px solid var(--border-hover);cursor:pointer}
.plan-cta.secondary:hover{border-color:var(--indigo);transform:translateY(-2px)}
.plan-cta svg{flex:0 0 16px}
/* `hidden` must win over the inline .plan-cta display:inline-flex. */
/* Yearly: the per-month headline is a price slot of its own (an empty one keeps the line's height), and the
   charge it comes from sits right under it, bold, at the terms' size. */
#yearlyPerMonth{min-height:1.25em}
.plan-billed{display:block;min-height:1.55em;color:var(--text);font-weight:700}
.plan-billed .amt[data-price-length]{font-size:inherit;line-height:inherit}
.plan-billed .amt+.per{margin-left:.3em}
/* Its skeleton is sized for a long figure at this small size (Rp 19.999.999,00), not for the headline's 3.5em. */
.plan-billed .amt:is([data-price-state="loading"],[data-price-state="unavailable"]){min-width:6.5em}
/* Price loading (upgrade.js setPriceSlot / setYearlyEquivalent). Every price line owns its space from
   the first paint, so a quote that arrives late, or an estimate replaced by the live one, moves
   nothing: the placeholders are skeletons of the final size, and a line with nothing to say keeps
   its box invisible instead of collapsing. State is data-price-state: loading | unavailable
   (placeholder), estimate | live (a price), ready (text) | none (reserved, empty). */
:root{--skeleton:rgba(148,163,184,.16);--skeleton-hi:rgba(148,163,184,.30)}
@keyframes priceShimmer{from{background-position:100% 0}to{background-position:-100% 0}}
.amt[data-price-state]{display:inline-block;position:relative}
.amt[data-price-state="loading"],.amt[data-price-state="unavailable"]{min-width:3.5em;color:transparent;user-select:none}
.amt[data-price-state="loading"]::before,.amt[data-price-state="unavailable"]::before{content:'';position:absolute;inset:.22em 0;border-radius:999px;background:linear-gradient(90deg,var(--skeleton) 0,var(--skeleton-hi) 50%,var(--skeleton) 100%) 0 0/200% 100%;animation:priceShimmer 1.4s linear infinite}
.amt[data-price-state="unavailable"]::before{animation:none;background:var(--skeleton)}
/* The unit waits for its price; hidden (not removed) so nothing around it moves. */
.amt[data-price-state="loading"]+.per,.amt[data-price-state="unavailable"]+.per{visibility:hidden}
/* Worst-case geometry. A figure never wraps and never breaks its neighbours' bidi order; a long one
   (CHF 12'345.00, Rp 1.999.999,00, Arabic digits and RTL marks) is drawn smaller INSIDE the same line box
   (the card's own line-height sets that box, so the height is the same for skeleton, estimate and live).
   On a phone the unit sits on its own line in every state, so a long figure cannot wrap it into view late. */
.amt{white-space:nowrap;unicode-bidi:isolate}
/* Top-aligned, with the line-height scaled back up (1.25 / size), so the figure's box keeps the line's own height and top
   edge: the skeleton and the smaller figure that replaces it occupy exactly the same place. */
.amt[data-price-state]{vertical-align:top}
.amt[data-price-length="m"]{font-size:.9em;line-height:1.389}
.amt[data-price-length="l"]{font-size:.75em;line-height:1.667}
.amt[data-price-length="xl"]{font-size:.62em;line-height:2.016}
/* The gap before the unit is a margin, not a space character: a text node would move (and count as a shift) when a long figure replaces the skeleton. */
.plan-price .amt+.per{margin-left:.3em}
/* "+ tax" after the unit where Paddle adds tax on top of the price (upgrade.js setTaxAdded): one unbreakable piece. */
.plan-tax{white-space:nowrap}
.plan-tax[hidden]{display:none}
@media(max-width:480px){.plan-price .per{display:block}.plan-price .amt+.per{margin-left:0}}
.visually-hidden{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
/* Wherever the card is narrow enough for a long currency's billed-yearly line to wrap: a phone, and three columns in a ~1000 px window. */
@media(max-width:414px),(min-width:960px) and (max-width:1100px){.plan-billed{min-height:3.1em}}
/* In three columns every card's terms take that reserved height too, so the lists still start on one line. */
@media(min-width:960px) and (max-width:1100px){.plan-sub{min-height:4.65em}}
/* The note holds two lines (three on a very narrow screen), the longest it ever says. */
.price-note{min-height:3.4em}
@media(max-width:360px){.price-note{min-height:5.1em}}
@media(prefers-reduced-motion:reduce){.amt[data-price-state]::before{animation:none!important}}
/* An active Lifetime launch offer fills the first line of the Lifetime terms (the line where Pro shows
   its per-month figure) once its launch quote is confirmed; upgrade.js marks the card, and the line
   keeps its space (invisible) until then. */
.plan-launch{display:block;min-height:1.55em;color:#10b981;font-weight:600}
.plan-launch[hidden]{display:none}
/* The comparison row above each price (owner, 1 November): a pill and a muted reference. Every card
   reserves the row, so the prices and lists stay level; Yearly fills it with its saving against Monthly
   (never struck: nobody paid twelve months of Monthly), an active Lifetime launch offer with its saving and
   the standard price struck through (owner, 2 November).
   The pill leads at a fixed width, so a quote that changes the reference figure moves nothing (CLS). */
.plan-was{display:flex;align-items:center;flex-wrap:wrap;gap:0 10px;min-height:30px;margin-top:6px;font-size:20px;line-height:1.3}
.plan-was~.plan-price{margin-top:0}
/* Where a long currency can wrap the row (narrow three-column and phone widths), it reserves two lines
   and keeps its content on the bottom line, next to the price. */
@media(max-width:414px),(min-width:960px) and (max-width:1100px){.plan-was{min-height:2.6em;align-content:flex-end}.plan-was-ref{flex-basis:100%}}
/* There the Monthly comparison always takes the second line, so a long figure replacing a short one never moves it under the pill. */
.plan-was-ref{color:var(--text-muted);font-size:15px;font-weight:600;overflow-wrap:anywhere}
.plan-was-ref[hidden],.plan-was-badge[hidden]{display:none}
/* The struck standard price must stay legible (owner, 3 November: "€28.99" read as 20 or 28): larger and
   brighter than the muted reference, with a thinner line so it cannot hide the waist of an 8 or a 3. */
.plan-was-struck{color:#c9cde3;font-size:20px;font-weight:700;letter-spacing:-.01em;font-variant-numeric:tabular-nums;text-decoration:line-through;text-decoration-thickness:1.5px;text-decoration-color:rgba(251,113,133,.9)}
.plan-was-badge{order:-1;box-sizing:border-box;min-width:4.4em;text-align:center;padding:3px 6px;border-radius:999px;background:rgba(16,185,129,.16);border:1px solid rgba(16,185,129,.45);color:#34d399;font-size:13px;font-weight:700;line-height:1.3;white-space:nowrap}
.plan-was-badge:empty{display:none}
#yearlyWas:not([data-price-state="ready"])>*{visibility:hidden}
.plan-lifetime[data-launch="pending"] #lifetimeLaunchNote[hidden]{display:block;visibility:hidden}
/* Where the per-month line gets two lines, so does this one: a long currency's standard price wraps it. */
@media(max-width:414px),(min-width:960px) and (max-width:1100px){.plan-launch{min-height:3.1em}}
/* An empty live region (upgrade.js installTrialEntry adds one beside each trial button) takes no
   place in the layout until it has something to say, but stays in the accessibility tree. */
.trial-inline-status:empty,.pricing-trial-entry .trial-inline-status:empty{position:absolute;width:1px;height:1px;margin:0;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
/* Trial-first, state-aware offer (owner, 19 October). upgrade.js sets html[data-offer]:
   "trial" (default — the trial may still be available) leads each Pro card with the
   ONE product-wide trial and turns its purchase button into the quiet, outlined member
   of the CTA family; "buy" (the extension's checkout handoff) and "paid" (a signed-in
   paid account) lead with buying as before. Absent the attribute, trial-first holds. */
.plan-actions{margin-top:auto;display:flex;flex-direction:column;gap:12px}
.plan-actions .plan-cta{margin-top:0}
.plan-trial-block{display:flex;flex-direction:column;gap:8px}
.plan-trial-note{margin:0;font-size:12px;line-height:1.5;color:var(--text-muted);text-align:center}
html:not([data-offer="buy"]):not([data-offer="paid"]) .plan-cta[data-buy]{background:rgba(255,255,255,.03);color:var(--text);border:1px solid var(--border-hover);box-shadow:none}
html:not([data-offer="buy"]):not([data-offer="paid"]) .plan-cta[data-buy]:not(:disabled):hover{border-color:var(--indigo);box-shadow:none}
html[data-offer="buy"] .plan-trial-block,html[data-offer="paid"] .plan-trial-block,html[data-offer="paid"] .pricing-trial-entry{display:none}
html:not([data-offer="buy"]) .trial-entry-offer,html:not([data-offer="buy"]) .pricing-trial-entry [data-trial-open],html[data-offer="buy"] .trial-entry-how{display:none}
/* The trial strip is the page's one free offer, so it is built to be seen, not to sit
   beside the price cards as a footnote (owner, 19 October: "it is dim — Get Lifetime jumps
   out and this does not"). The free offer gets its own colour — emerald into cyan — so it
   reads as "free" and never as a second purchase button in the indigo purchase gradient. */
@property --trial-angle{syntax:'<angle>';inherits:false;initial-value:0deg}
.plan-trial{position:relative;overflow:hidden;isolation:isolate;display:inline-flex;align-items:center;justify-content:center;gap:9px;min-height:50px;padding:12px 22px;border:0;border-radius:13px;background:linear-gradient(135deg,#10b981,#06b6d4 58%,#6366f1);color:#fff;font:inherit;font-size:15px;font-weight:750;letter-spacing:-.1px;cursor:pointer;box-shadow:0 0 0 1px rgba(255,255,255,.14) inset,0 10px 28px -8px rgba(16,185,129,.7),0 0 40px -6px rgba(34,211,238,.45);transition:transform .18s ease,box-shadow .18s ease,filter .18s ease}
.plan-trial svg{flex:0 0 18px;width:18px;height:18px}
/* The label is text, a [data-trial] span and text: three flex items. Space them like words, not like a toolbar. */
.plan-trial,.plan-trial-cta{gap:0}
.plan-trial svg,.plan-trial-cta svg{margin-right:9px}
.plan-trial [data-trial="adj"],.plan-trial-cta [data-trial="adj"]{margin:0 .28em}
.plan-trial::after{content:'';position:absolute;inset:0;z-index:-1;background:linear-gradient(105deg,transparent 30%,rgba(255,255,255,.45) 48%,transparent 66%);transform:translateX(-120%);animation:trialShine 3.6s ease-in-out 1.2s infinite}
.plan-trial:hover{transform:translateY(-2px);filter:saturate(1.15) brightness(1.06);box-shadow:0 0 0 1px rgba(255,255,255,.2) inset,0 16px 36px -8px rgba(16,185,129,.85),0 0 56px -4px rgba(34,211,238,.6)}
.plan-trial:active{transform:translateY(0)}
@keyframes trialShine{0%{transform:translateX(-120%)}38%,100%{transform:translateX(120%)}}
.plan-trial:focus-visible,.plan-cta:focus-visible,.pricing-trial-actions a:focus-visible{outline:2px solid var(--cyan);outline-offset:4px;border-radius:8px}
.pricing-trial-entry{position:relative;display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:16px 24px;max-width:860px;margin:0 auto 28px;padding:22px 26px;border:1.5px solid transparent;border-radius:18px;background:linear-gradient(135deg,rgba(16,185,129,.13),rgba(13,17,33,.96) 42%,rgba(99,102,241,.14)) padding-box,linear-gradient(#0d1121,#0d1121) padding-box,conic-gradient(from var(--trial-angle),#10b981,#22d3ee,#6366f1,#a855f7,#22d3ee,#10b981) border-box;box-shadow:0 22px 70px -28px rgba(16,185,129,.55),0 0 0 6px rgba(34,211,238,.04);animation:trialRing 7s linear infinite}
@keyframes trialRing{to{--trial-angle:360deg}}
/* The gift is a quiet outline on a faint tint: the filled gradient (and its glow) belongs to the Start button alone. */
.trial-entry-icon{display:grid;place-items:center;width:52px;height:52px;border-radius:15px;background:rgba(255,255,255,.04);color:#a7f3d0;border:1px solid rgba(255,255,255,.14)}
.trial-entry-icon svg{width:26px;height:26px}
.trial-entry-badge{display:inline-block;margin:0 0 6px;padding:3px 10px;border-radius:999px;background:rgba(16,185,129,.16);color:#34d399;font-size:11px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;box-shadow:0 0 0 1px rgba(52,211,153,.3) inset}
.trial-entry-text{min-width:0}
.pricing-trial-entry p{margin:0;font-size:14.5px;line-height:1.6;color:var(--text-secondary)}
.pricing-trial-entry strong{color:var(--text)}
.pricing-trial-actions{display:flex;flex-direction:column;align-items:center;gap:10px}
.pricing-trial-actions a{font-size:13px;color:var(--text-secondary);text-underline-offset:4px}
.pricing-trial-actions a:hover{color:var(--text)}
/* One-click Trial (upgrade.js installTrialEntry): the Start button asks the extension to
   open Account and, when it does, says so right here instead of opening a modal. The
   live region exists empty from load; it only takes up room once it has something to say. */
[data-trial-open][aria-busy="true"]{cursor:progress}
.trial-inline-status{max-width:52ch;margin:14px 0 0;font-size:13px;line-height:1.55;color:var(--text-secondary)}
.trial-inline-status[data-trial-state="opened"]{padding:8px 12px;border-left:2px solid #10b981;border-radius:0 8px 8px 0;background:rgba(16,185,129,.08);color:var(--text)}
.pricing-trial-entry .trial-inline-status{grid-column:1/-1;max-width:none}
@media(max-width:959px){.plan-grid{grid-template-columns:minmax(0,1fr);max-width:480px;margin:auto}.plan-head,.plan-sub,.plan-price{min-height:0}.plan-price{font-size:36px}.plan-was{font-size:18px}.plan-monthly>.plan-was,.plan-lifetime:not([data-launch])>.plan-was{display:none}}
/* Below ~760 px the actions drop under icon + text, full width; on a phone everything is one centred column. */
@media(max-width:759px){.pricing-trial-entry{grid-template-columns:auto minmax(0,1fr)}.pricing-trial-actions{grid-column:1/-1;align-items:stretch;text-align:center}.pricing-trial-actions .plan-trial{width:100%}}
@media(max-width:599px){.plan{padding:26px 22px}.pricing-trial-entry{padding:18px}.trial-entry-icon{width:44px;height:44px;border-radius:13px}}
@media(max-width:479px){.pricing-trial-entry{grid-template-columns:minmax(0,1fr);justify-items:center;text-align:center;gap:14px}.trial-entry-icon{width:40px;height:40px;border-radius:12px}.trial-entry-icon svg{width:22px;height:22px}.pricing-trial-actions{width:100%}}
.trial-dialog{width:min(560px,calc(100% - 28px));max-height:calc(100dvh - 32px);margin:auto;padding:32px;border:1px solid var(--border-hover);border-radius:22px;color:var(--text);background:linear-gradient(145deg,#1b2240,#101629);box-shadow:0 30px 100px #0009;overflow-y:auto;font:inherit}
.trial-dialog::backdrop{background:rgba(7,10,22,.8)}
.trial-dialog[open]{animation:trialDialogIn .18s ease-out}
.trial-dialog h2{font-size:30px;line-height:1.15;letter-spacing:-.7px;margin:14px 24px 18px 0}
.trial-dialog p{font-size:14px;line-height:1.7;color:var(--text-secondary);margin:14px 0}
.trial-dialog strong{color:var(--text)}
.trial-dialog ul,.trial-dialog ol{padding-left:22px;font-size:14px;line-height:1.7}
.trial-dialog li+li{margin-top:8px}
.trial-dialog-close{position:absolute;right:12px;top:10px;width:40px;height:40px;border:0;border-radius:10px;color:var(--text-secondary);background:transparent;font:inherit;font-size:28px;cursor:pointer}
.trial-dialog-close:hover{background:var(--bg-elevated);color:var(--text)}
.trial-eyebrow{color:var(--cyan);font-weight:700;font-size:11px;letter-spacing:.12em;text-transform:uppercase}
.trial-dialog .trial-dialog-status{border-left:2px solid var(--cyan);padding:8px 12px;background:#06b6d409;color:var(--text);font-size:13px}
.trial-dialog-actions{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-top:24px}
.trial-start{font:inherit;font-weight:700;font-size:14px;display:inline-flex;justify-content:center;align-items:center;min-height:48px;padding:12px 20px;border:0;border-radius:12px;background:var(--gradient);color:white;cursor:pointer;text-decoration:none}
.trial-dialog-actions a{font-size:13px;color:var(--text-secondary);text-underline-offset:4px}
.trial-dialog-actions:not(:has(> :not([hidden]))){display:none}
.trial-dialog-done{font:inherit;font-weight:700;font-size:14px;min-height:48px;padding:12px 24px;border:1px solid var(--border-hover);border-radius:12px;background:rgba(255,255,255,.04);color:var(--text);cursor:pointer}
.trial-dialog-done:hover{border-color:var(--cyan)}
.trial-dialog[data-trial-state="opened"] .trial-dialog-status{border-left-color:#10b981;background:rgba(16,185,129,.08)}
.trial-dialog[data-trial-state="manual"] .trial-dialog-status{border-left-color:var(--amber)}
.trial-dialog .trial-dialog-footnote{font-size:12px;margin-bottom:0}
.trial-native-steps{margin-top:18px;padding-top:18px;border-top:1px solid var(--border)}
.trial-native-steps h3{font-size:17px;margin-bottom:14px}
.trial-dialog [hidden]{display:none!important}
.trial-dialog :focus-visible,.trial-start:focus-visible{outline:2px solid var(--cyan);outline-offset:4px}
/* The title takes focus only so the dialog opens on its own name; it is not a control. */
.trial-dialog h2[tabindex="-1"]:focus{outline:none}
@keyframes trialDialogIn{from{opacity:.4;transform:translateY(8px)}to{opacity:1;transform:none}}
@media(max-width:420px){.trial-dialog{padding:24px 20px}.trial-dialog h2{font-size:26px}.trial-dialog-actions{align-items:stretch;flex-direction:column}}
@media(prefers-reduced-motion:reduce){.pricing-trial-entry,.plan-trial::after{animation:none}.plan-trial,.plan-trial:hover{transition:none;transform:none}.trial-dialog[open]{animation:none}.plan-cta{transition:none}.plan-cta:hover{transform:none}}
.plan .feat{list-style:none}
/* Learn-more links inside a feature list read as the list's own text, with a
   quiet dotted underline as the only hint; they light up on hover and focus. */
.plan .feat .feat-more{color:inherit;text-decoration:underline dotted rgba(163,168,195,.32);text-underline-offset:4px;text-decoration-thickness:1px}
.plan .feat .feat-more:hover{color:var(--text);text-decoration-color:var(--cyan)}
.plan .feat .feat-more:focus-visible{outline:2px solid var(--cyan);outline-offset:2px;border-radius:4px}
/* Block, not the inline sheet's flex: a list item that mixes text and learn-more
   links must wrap as one sentence, not as separate flex columns. */
.plan .feat li{position:relative;display:block;padding:0 0 0 18px}
.plan .feat li::before{content:'✓';position:absolute;left:0;color:var(--cyan);font-size:12px}
/* Paused trial (Billing's trial_enabled false). The scripts set the hidden attribute
   AND data-trial-hidden / html[data-trial-paused], but author display rules such as
   .pricing-trial-entry{display:flex} beat [hidden]; these two rules make the state
   authoritative on every page that loads this sheet. */
html[data-trial-paused] [data-trial-open],html[data-trial-paused] [data-trial-offer]{display:none!important}
/* A trial that is running, spent or beside a purchase (upgrade.js: the site bridge's answer or the
   extension's ?trial= hint) is not offered again. Same authority as the paused rule above. */
/* A visitor who has the extension (upgrade.js: it opened this page or its bridge answered) sees their header
   button read "Added to Chrome" (dark-theme.css, .bi-done). Nothing else on the page is hidden for them: the box for
   buying on the website stays available, so the page shows state rather than removing choices (owner, 21 October 2026). */
html[data-trial-state="hidden"] [data-trial-open],html[data-trial-state="hidden"] [data-trial-offer]{display:none!important}
[data-trial-hidden]{display:none!important}
/* The Pro trial is core-only (ADR-016): where a fresh install gets trial runs or days (upgrade.js sets
   html[data-access] from GET /v1/paywall/public), no page offers the separate Pro trial. */
html[data-access="runs"] [data-trial-open],html[data-access="runs"] [data-trial-offer],html[data-access="time"] [data-trial-open],html[data-access="time"] [data-trial-offer]{display:none!important}

/* A disabled buy button (Paddle not ready, or no sign-in yet) must still be readable: the label is
   the explanation, and half-transparent text on a dark card is what read as faded. */
.plan-cta.primary:disabled{opacity:.62}
html:not([data-offer="buy"]):not([data-offer="paid"]) .plan-cta[data-buy]:disabled{opacity:.8}

/* The trial banner collapses over a short height and fade when the extension's answer arrives after the page
   was drawn (upgrade.js collapseTrialEntry). .collapsing keeps it displayed while data-trial-state="hidden"
   would otherwise remove it; the hint path never runs this (the banner is simply absent from the first paint). */
.pricing-trial-entry.collapsing{overflow:hidden;animation:none;transition:max-height .3s ease,opacity .24s ease,margin .3s ease,padding .3s ease,border-width .3s ease}
html[data-trial-state="hidden"] .pricing-trial-entry.collapsing{display:grid!important}
.pricing-trial-entry.collapsing.collapsing-go{max-height:0!important;opacity:0;margin-top:0;margin-bottom:0;padding-top:0;padding-bottom:0;border-width:0}
@media(prefers-reduced-motion:reduce){.pricing-trial-entry.collapsing{transition:none}}
/* A paid seat: a calm note above the prices, and buy buttons that stop leading. */
.pro-active-notice{display:none;align-items:center;justify-content:center;gap:10px;max-width:640px;margin:0 auto 28px;padding:14px 20px;border-radius:14px;background:rgba(16,185,129,.1);border:1px solid rgba(16,185,129,.3);font-size:14px;color:#d3d7ec;text-align:center;line-height:1.6}
html[data-pro-active] .pro-active-notice{display:flex}
.pro-active-notice svg{flex-shrink:0}
.pro-active-notice a{color:#a5b4fc;text-decoration:underline;text-underline-offset:3px}
html[data-pro-active] .plan-cta[data-buy]{background:rgba(129,140,248,.12);color:#fff;border:1px solid rgba(165,180,252,.55);box-shadow:none}
html[data-pro-active] .plan-cta[data-buy]:not(:disabled):hover{background:rgba(129,140,248,.22);border-color:#a5b4fc;box-shadow:none;transform:none}

/* ==== U2: the calm cards with a light polish (owner, 22 October 2026: "as it was, a touch nicer, nothing loud") ====
   Monthly stays quiet; Yearly, the recommended card, gets a thin cyan edge; Lifetime keeps its violet one. */
.plan-badge.plan-badge-quiet{background:rgba(168,85,247,.16);border:1px solid rgba(168,85,247,.45);color:#e9d5ff}
.plan .plan-name{color:#e9ebf7}
.plan.pro.plan-yearly{border-color:rgba(34,211,238,.38);box-shadow:0 1px 0 rgba(255,255,255,.05) inset,0 24px 60px -40px rgba(34,211,238,.45)}
.plan.plan-lifetime{box-shadow:0 1px 0 rgba(255,255,255,.05) inset,0 24px 60px -40px rgba(168,85,247,.5)}
.plan-yearly .plan-price .amt:is([data-price-state="estimate"],[data-price-state="live"]){color:#fff}
.plan .feat li::before{color:#22d3ee;font-weight:700}
/* Paddle checkout overlay. Paddle.js places its frame with one inline style attribute, which this page's
   CSP refuses (style-src-attr 'none'), so the frame fell to the end of the page at the browser's default
   300x150 (owner, 31 October). This stylesheet gives it the geometry Paddle asks for, without unsafe-inline. */
iframe.paddle-frame-overlay{display:block;position:fixed;inset:0;width:100%;height:100%;margin:0;padding:0;border:0;background:transparent;z-index:2147483647}
/* Google sign-in (owner, 31 October): one first-party button that opens Google directly. It follows Google's
   own light button (white, grey outline, the four-colour G, "Continue with Google") so a visitor recognises it,
   and it is styled here, not by Google's script, because this page's CSP refuses the inline styles GIS injects. */
.signin-google-trigger{display:inline-flex;align-items:center;justify-content:center;gap:10px;min-height:40px;padding:0 16px 0 12px;background:#fff;color:#1f1f1f;border:1px solid #747775;border-radius:20px;font-size:14px;font-weight:500;letter-spacing:.01em;line-height:1}
.signin-google-trigger:hover{background:#f6f8fe;border-color:#747775;box-shadow:0 1px 2px rgba(60,64,67,.3),0 1px 3px 1px rgba(60,64,67,.15)}
.signin-google-trigger:focus-visible{outline:2px solid #a5b4fc;outline-offset:2px}
.signin-google-trigger:disabled{opacity:.6;cursor:wait}
.signin-google-mark{display:block;width:18px;height:18px;flex-shrink:0}
.signin-switch{margin:8px 0 0!important;text-align:center}
.signin-signout{padding:4px 8px;background:none;border:0;color:#a5b4fc;font:inherit;font-size:12.5px;text-decoration:underline;text-underline-offset:3px;cursor:pointer}
.signin-signout:hover{color:#c7d2fe}
.signin-signout:focus-visible{outline:2px solid #a5b4fc;outline-offset:2px;border-radius:4px}

/* No extension checkout and no session: how to buy, above the cards (upgrade.html #noCorrelationNotice). */
.buy-from-extension {
  display: none; max-width: 640px; margin: 0 auto 22px; padding: 14px 20px; border-radius: 14px; text-align: center;
  background: rgba(245,158,11,.07); border: 1px solid rgba(245,158,11,.28);
}
/* Shown from the first paint for a visit without the extension's checkout (html[data-buy-instructions], set in
   <head> by upgrade.js), so it never pushes the cards down later; `hidden` takes it away, and so does Pro. */
html[data-buy-instructions]:not([data-pro-active]) .buy-from-extension:not([hidden]) { display: block; }
.buy-from-extension p { font-size: 14px; color: var(--text-secondary); line-height: 1.6; margin: 0; }
.buy-from-extension strong { color: var(--text-primary, #fff); }
.buy-from-extension a { color: var(--indigo); text-decoration: none; }
.buy-from-extension a:hover { text-decoration: underline; }
