/*
 * Product page preview — /produkt-claude-preview.
 *
 * GEO Site Audit widget (widgets/audit-crawl.blade.php). Every rule below describes the finished
 * report; the sequence that leads to it is a set of keyframe animations whose "from" frames hold
 * the earlier states. They only apply once the widget is armed by its inline script, sit paused
 * until marketing.js marks the widget live, and restart by themselves each time the accordion
 * shows the widget again (display: none cancels CSS animations).
 *
 * Timeline (s): 0–1.2 map the site · 1.3–2.0 select areas · 2.05–3.9 scan pages · 3.85 report.
 */

@property --crawl-n { syntax: '<integer>'; inherits: false; initial-value: 0; }
@property --crawl-t { syntax: '<number>'; inherits: false; initial-value: 0; }

.mkt-crawl { gap: 12px; container-type: inline-size; }

.mkt-crawl-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.mkt-crawl-num { --crawl-n: var(--to); counter-reset: crawl var(--crawl-n); }
.mkt-crawl-num::after { content: counter(crawl); }

/* --- status line ------------------------------------------------------------ */

.mkt-crawl-status { display: flex; align-items: center; gap: 10px; min-height: 20px; }
.mkt-crawl-state { position: relative; width: 15px; height: 15px; flex: 0 0 auto; }
.mkt-crawl-spinner {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid #E3E4FA;
  border-top-color: #5B5CE6;
  opacity: 0;
}
.mkt-crawl-tick { position: absolute; inset: 0; border-radius: 50%; background: #22C55E; }
.mkt-crawl-tick::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 3.2px;
  width: 3.6px;
  height: 6.6px;
  border: solid #FFFFFF;
  border-width: 0 1.7px 1.7px 0;
  transform: rotate(45deg);
}
.mkt-crawl-steps { display: grid; flex: 1; min-width: 0; font-size: 13px; font-weight: 500; color: #262A47; }
.mkt-crawl-steps > span { grid-area: 1 / 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; opacity: 0; }
.mkt-crawl-steps > .is-done { opacity: 1; }
.mkt-crawl-found { font-family: 'IBM Plex Mono', monospace; font-size: 11px; color: #656B7E; white-space: nowrap; }
.mkt-crawl-found b { font-weight: 600; color: #14162B; }
.mkt-crawl-bar { height: 3px; margin-top: -4px; border-radius: 3px; background: #EDEEF2; overflow: hidden; }
.mkt-crawl-bar i { display: block; height: 100%; background: #5B5CE6; transform-origin: 0 50%; }

/* --- site map ----------------------------------------------------------------
 * Positions come from the markup as percentages; labels are sized in cqw, so the whole map
 * scales with its width like a drawing while the text stays live, crisp HTML. */

.mkt-crawl-map {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  container-type: inline-size;
  border: 1px solid #EDEEF2;
  border-radius: 12px;
  background-color: #FCFCFE;
  background-image: radial-gradient(circle, #E4E6EF 1px, transparent 1.4px);
  background-size: 16px 16px;
  overflow: hidden;
}
.mkt-crawl-edges { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.mkt-crawl-edge {
  stroke: #D5D8E2;
  stroke-width: 1.25;
  stroke-dasharray: 3 4;
  vector-effect: non-scaling-stroke;
  transform-box: view-box;
  transform-origin: 50% 51%;
}
.mkt-crawl-edge.is-selected { stroke: #B4B6F1; stroke-width: 1.5; stroke-dasharray: none; }

.mkt-crawl-root {
  position: absolute;
  left: 50%;
  top: 51%;
  translate: -50% -50%;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 0.5em;
  padding: 0.55em 1em;
  border-radius: 999px;
  background: #14162B;
  color: #FFFFFF;
  font-family: 'IBM Plex Mono', monospace;
  font-size: clamp(9px, 2.5cqw, 13px);
  font-weight: 600;
  white-space: nowrap;
  box-shadow: 0 10px 24px -12px rgba(20, 22, 43, 0.6);
}
.mkt-crawl-root svg { width: 1.15em; height: 1.15em; flex: 0 0 auto; }
.mkt-crawl-halo {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 0 0 2px rgba(91, 92, 230, 0.6);
  opacity: 0;
  pointer-events: none;
}

.mkt-crawl-pulse {
  position: absolute;
  left: calc(50% + var(--dx) * var(--crawl-t) * 1%);
  top: calc(51% + var(--dy) * var(--crawl-t) * 1%);
  translate: -50% -50%;
  z-index: 1;
  width: max(6px, 1.5cqw);
  height: max(6px, 1.5cqw);
  border-radius: 50%;
  background: #5B5CE6;
  box-shadow: 0 0 0 max(3px, 0.8cqw) rgba(91, 92, 230, 0.18);
  opacity: 0;
  pointer-events: none;
}

.mkt-crawl-area { position: absolute; translate: -50% -50%; z-index: 1; }
.mkt-crawl-card {
  display: grid;
  grid-template-columns: auto auto auto;
  align-items: center;
  column-gap: 0.55em;
  row-gap: 0.5em;
  padding: 0.6em 0.75em 0.7em;
  border: 1px solid #C7C8F6;
  border-radius: 0.8em;
  background: #FFFFFF;
  box-shadow: 0 8px 18px -14px rgba(20, 22, 43, 0.5);
  font-size: clamp(8.5px, 2.35cqw, 12.5px);
  line-height: 1.2;
}
.mkt-crawl-check { position: relative; width: 1.15em; height: 1.15em; border-radius: 0.3em; background: #5B5CE6; }
.mkt-crawl-check::after {
  content: "";
  position: absolute;
  left: 0.39em;
  top: 0.17em;
  width: 0.3em;
  height: 0.58em;
  border: solid #FFFFFF;
  border-width: 0 0.13em 0.13em 0;
  transform: rotate(45deg);
}
.mkt-crawl-path { font-family: 'IBM Plex Mono', monospace; font-weight: 600; color: #14162B; white-space: nowrap; }
.mkt-crawl-count {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.85em;
  color: #5F6478;
  background: #F1F2F6;
  border-radius: 999px;
  padding: 0.1em 0.5em;
}
.mkt-crawl-pages { grid-column: 1 / -1; display: flex; gap: 0.36em; }
.mkt-crawl-pages i { width: 0.66em; height: 0.66em; border-radius: 50%; background: #22C55E; }
.mkt-crawl-pages i.is-issue { background: #F59E0B; }
.mkt-crawl-pages i.is-pending { background: #D5D8E2; }

/* Areas left out of this run: dashed, muted, empty checkbox — still "to scan". */
.mkt-crawl-area:not(.is-selected) .mkt-crawl-card { border-style: dashed; border-color: #CDD0DB; background: #FAFAFC; box-shadow: none; }
.mkt-crawl-area:not(.is-selected) .mkt-crawl-check { background: #FFFFFF; box-shadow: inset 0 0 0 1.5px #C4C8D6; }
.mkt-crawl-area:not(.is-selected) .mkt-crawl-check::after { opacity: 0; }
.mkt-crawl-area:not(.is-selected) .mkt-crawl-path { color: #7C8196; }

/* Phone-width maps are short and wide: smaller labels without the page-count pill, so the
   areas on the lower half don't run into each other. */
@container (max-width: 320px) {
  .mkt-crawl-card { font-size: clamp(7.5px, 2.6cqw, 8.5px); padding: 0.55em 0.7em 0.65em; }
  .mkt-crawl-count { display: none; }
  .mkt-crawl-root { font-size: clamp(8px, 2.8cqw, 9px); }
}

/* --- report ------------------------------------------------------------------ */

.mkt-crawl-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px 18px; flex-wrap: wrap; }
.mkt-crawl-score { display: flex; align-items: center; gap: 11px; min-width: 0; flex: 1 1 220px; }
.mkt-crawl-ring { position: relative; width: 46px; height: 46px; flex: 0 0 auto; }
.mkt-crawl-ring svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
.mkt-crawl-ring circle { fill: none; stroke-width: 4; }
.mkt-crawl-ring .is-track { stroke: #EDEEF2; }
.mkt-crawl-ring .is-value { stroke: #22C55E; stroke-linecap: round; stroke-dasharray: 100; stroke-dashoffset: calc(100 - var(--score)); }
.mkt-crawl-ring .mkt-crawl-num {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  font-weight: 600;
  color: #14162B;
}
.mkt-crawl-score-copy { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.mkt-crawl-score-copy strong { font-size: 13.5px; font-weight: 600; color: #14162B; }
.mkt-crawl-score-copy span { font-size: 11.5px; line-height: 1.4; color: #656B7E; text-wrap: pretty; }

.mkt-crawl-stats { display: flex; gap: 16px; margin: 0; }
.mkt-crawl-stats div { display: flex; flex-direction: column; gap: 3px; }
.mkt-crawl-stats dt {
  display: flex;
  align-items: center;
  gap: 5px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #656B7E;
  white-space: nowrap;
}
.mkt-crawl-stats dt::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: #22C55E; }
.mkt-crawl-stats .is-issue dt::before { background: #F59E0B; }
.mkt-crawl-stats .is-pending dt::before { background: #D5D8E2; }
.mkt-crawl-stats dd { margin: 0; font-size: 17px; font-weight: 600; line-height: 1.1; color: #14162B; }

/* Narrow previews: one row of report — the ring and the three counts — so the map keeps its height. */
@container (max-width: 440px) {
  .mkt-crawl-found-label { display: none; }
  .mkt-crawl-foot { flex-wrap: nowrap; gap: 12px; }
  .mkt-crawl-score { flex: 0 0 auto; }
  .mkt-crawl-score-copy { display: none; }
  .mkt-crawl-ring { width: 40px; height: 40px; }
  .mkt-crawl-ring .mkt-crawl-num { font-size: 13px; }
  .mkt-crawl-stats { flex: 1; justify-content: space-between; gap: 8px; }
  .mkt-crawl-stats dt { font-size: 8.5px; letter-spacing: 0.04em; }
  .mkt-crawl-stats dd { font-size: 15px; }
}

/* --- sequence ---------------------------------------------------------------- */

.mkt-crawl.is-armed .mkt-crawl-root { animation: crawl-pop 0.4s cubic-bezier(0.2, 0.9, 0.3, 1.2) both; }
.mkt-crawl.is-armed .mkt-crawl-edge { animation: crawl-grow 0.45s cubic-bezier(0.22, 1, 0.36, 1) calc(0.15s + var(--i) * 0.07s) both; }
.mkt-crawl.is-armed .mkt-crawl-edge.is-selected {
  animation:
    crawl-grow 0.45s cubic-bezier(0.22, 1, 0.36, 1) calc(0.15s + var(--i) * 0.07s) both,
    crawl-edge-pick 0.35s ease calc(1.35s + var(--k) * 0.12s) backwards;
}
.mkt-crawl.is-armed .mkt-crawl-area { animation: crawl-pop 0.4s cubic-bezier(0.2, 0.9, 0.3, 1.2) calc(0.3s + var(--i) * 0.07s) both; }
.mkt-crawl.is-armed .mkt-crawl-area.is-selected .mkt-crawl-card { animation: crawl-card-pick 0.35s ease calc(1.35s + var(--k) * 0.12s) backwards; }
.mkt-crawl.is-armed .mkt-crawl-area.is-selected .mkt-crawl-check { animation: crawl-check-pick 0.35s cubic-bezier(0.2, 0.9, 0.3, 1.3) calc(1.35s + var(--k) * 0.12s) backwards; }
.mkt-crawl.is-armed .mkt-crawl-area.is-selected .mkt-crawl-check::after { animation: crawl-fade-in 0.2s ease calc(1.45s + var(--k) * 0.12s) backwards; }
.mkt-crawl.is-armed .mkt-crawl-area:not(.is-selected) .mkt-crawl-card { animation: crawl-card-skip 0.4s ease 1.8s backwards; }
.mkt-crawl.is-armed .mkt-crawl-pages i[style] { animation: crawl-dot 0.34s ease calc(2.1s + var(--o) * 0.085s) backwards; }
.mkt-crawl.is-armed .mkt-crawl-pulse { animation: crawl-travel 0.8s cubic-bezier(0.4, 0, 0.2, 1) calc(2.05s + var(--k) * 0.14s) 2; }
.mkt-crawl.is-armed .mkt-crawl-halo { animation: crawl-halo 1s ease-out 2.05s 2; }

.mkt-crawl.is-armed .mkt-crawl-spinner { animation: crawl-spin 0.7s linear infinite, crawl-hold 3.85s linear both; }
.mkt-crawl.is-armed .mkt-crawl-tick { animation: crawl-pop 0.35s cubic-bezier(0.2, 0.9, 0.3, 1.3) 3.85s both; }
.mkt-crawl.is-armed .mkt-crawl-steps > .is-map { animation: crawl-fade-out 0.2s ease 1.25s both; }
.mkt-crawl.is-armed .mkt-crawl-steps > .is-pick { animation: crawl-window 0.75s ease 1.3s both; }
.mkt-crawl.is-armed .mkt-crawl-steps > .is-scan { animation: crawl-window 1.85s ease 2.05s both; }
.mkt-crawl.is-armed .mkt-crawl-steps > .is-done { animation: crawl-rise 0.3s ease 3.66s both; }
.mkt-crawl.is-armed .mkt-crawl-bar i { animation: crawl-bar 3.9s linear both; }

.mkt-crawl.is-armed .mkt-crawl-found .mkt-crawl-num { animation: crawl-count 1s ease-out 0.3s both; }
.mkt-crawl.is-armed .mkt-crawl-stats .is-pending .mkt-crawl-num { animation: crawl-count 0.5s ease-out 1.8s both; }
.mkt-crawl.is-armed .mkt-crawl-stats .is-scanned .mkt-crawl-num { animation: crawl-count 1.7s linear 2.1s both; }
.mkt-crawl.is-armed .mkt-crawl-stats .is-issue .mkt-crawl-num { animation: crawl-count 1.5s linear 2.3s both; }
.mkt-crawl.is-armed .mkt-crawl-score { animation: crawl-fade-in 0.35s ease 3.8s both; }
.mkt-crawl.is-armed .mkt-crawl-ring .is-value { animation: crawl-ring 0.9s cubic-bezier(0.22, 1, 0.36, 1) 3.85s both; }
.mkt-crawl.is-armed .mkt-crawl-ring .mkt-crawl-num { animation: crawl-count 0.9s cubic-bezier(0.22, 1, 0.36, 1) 3.85s both; }

/* Waiting for marketing.js to mark the widget live: hold the first frame. */
.mkt-widget:not(.is-live) .mkt-crawl.is-armed *,
.mkt-widget:not(.is-live) .mkt-crawl.is-armed *::after { animation-play-state: paused !important; }

@media (prefers-reduced-motion: reduce) {
  .mkt-crawl *,
  .mkt-crawl *::after { animation: none !important; }
}

@keyframes crawl-pop { from { opacity: 0; scale: 0.85; } }
@keyframes crawl-grow { from { transform: scale(0); } }
@keyframes crawl-edge-pick { from { stroke: #D5D8E2; stroke-width: 1.25; } }
@keyframes crawl-card-pick { from { border-color: #DEE0E8; box-shadow: none; } }
@keyframes crawl-check-pick { from { background: #FFFFFF; box-shadow: inset 0 0 0 1.5px #C4C8D6; scale: 1; } 60% { scale: 1.25; } }
@keyframes crawl-card-skip { from { border-color: #DEE0E8; border-style: solid; background: #FFFFFF; } }
@keyframes crawl-dot { 0% { background: #D5D8E2; scale: 1; } 55% { scale: 1.7; } 100% { scale: 1; } }
@keyframes crawl-travel { 0% { --crawl-t: 0; opacity: 0; } 15%, 85% { opacity: 1; } 100% { --crawl-t: 1; opacity: 0; } }
@keyframes crawl-halo { from { opacity: 0.9; scale: 1; } to { opacity: 0; scale: 1.25 1.9; } }
@keyframes crawl-spin { to { rotate: 360deg; } }
@keyframes crawl-hold { 0%, 96% { opacity: 1; } 100% { opacity: 0; } }
@keyframes crawl-fade-in { from { opacity: 0; } }
@keyframes crawl-fade-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes crawl-window { 0% { opacity: 0; translate: 0 4px; } 12%, 88% { opacity: 1; translate: 0 0; } 100% { opacity: 0; translate: 0 -4px; } }
@keyframes crawl-rise { from { opacity: 0; translate: 0 4px; } to { opacity: 1; translate: 0 0; } }
@keyframes crawl-bar { 0% { transform: scaleX(0); } 32% { transform: scaleX(0.3); } 53% { transform: scaleX(0.34); } 98%, 100% { transform: scaleX(1); } }
@keyframes crawl-count { from { --crawl-n: 0; } }
@keyframes crawl-ring { from { stroke-dashoffset: 100; } }


/* === citations ============================================================= */
/*
 * Product page preview — Monitoring → "Źródła i cytowania" (widgets/v2/citations.blade.php).
 *
 * Every rule below describes the FINISHED view: the citation-share donut with its own / third
 * party bars and six rows of the "Adresy źródłowe" table (source URL · mentions · the models that
 * cite it). No sentence under the table interprets them: the owner cut that reading as one more
 * line of text on a frame the numbers already explain. The sequence that leads there is a set of
 * keyframes whose early frames hold the earlier states. They apply only once the widget is armed
 * by its inline script, sit paused until marketing.js marks the widget live, and restart by
 * themselves each time the accordion shows the widget again (display: none cancels CSS
 * animations).
 *
 * Timeline (s): 0–0.38 filter chips · 0.05–0.08 table head + row skeleton, so the frame is never
 *               an empty box · 0.12–1.05 the donut sweeps, and at 0.36 the whole share band —
 *               both rows, both bars, both per cents — comes in on one schedule, because the two
 *               halves are one whole · 0.5–1.7 the six rows land, each bar growing and each count
 *               ticking · 1.70 grouped: the tick pops and the result line arrives and stays.
 * The status windows are laid END TO END (0–0.88 · 0.88–1.70 · 1.70 on): every handover is
 * sequential, never a cross-fade, so two readings of the status slot are never legible at
 * once. Each cz-window fades out over its last 12 % and in over its first 12 %, which costs one
 * near-blank frame at each handover — the same trade the sibling query-list and leaderboard
 * status lines make, and far cheaper than two sentences printed over each other.
 *
 * The status line narrates and then SETTLES on the result ("Lider cytowań - nike.com/running"),
 * not on a "Monitoring zaktualizowany" alert: the result span and the tick are visible in their
 * base state, so the no-JS and reduced-motion frames land straight on it (see the sequence block).
 *
 * What this preview deliberately does NOT draw, though the app's table has it — the frame has to
 * be readable in three seconds, and each of these costs a column without paying for it:
 *   · row numbering — the table is sorted by mentions and the bars already show the order;
 *   · "Unikalne zapytania" — a second, weaker copy of the mentions ranking (9·7·5·4·3·2);
 *   · "Data" (last mention) — every row falls inside the 8-day window the date chip states, so
 *     the column is six near-identical numbers;
 *   · the raw 44 / 84 next to the share bars — the ring's 128 and the two per cents give them;
 *   · the DataGrid pager — "Pokazano N z 36" is furniture on a teaser, and dropping it took the
 *     CSS row counter and its per-breakpoint bookkeeping with it;
 *   · the leading own/third colour dot — a fourth copy of one bit that the row's lilac band, the
 *     host's colour and the bar's colour already carry, and it held every host 10px right of the
 *     "URL" header. Gone, the column lines up with its own heading.
 * Six rows with room to breathe replaced ten squeezed ones for the same reason, and the rows
 * take the spare height of their bucket between them rather than banking it in a strip under the
 * last one (see the table block). Two of the six are the brand's own pages — the top one carries
 * 18 of the brand's citations and the second only 3, which is what the assistant's answer further
 * down the page (marketing.php) means by "praktycznie tylko nike.com/running (18)".
 * Six rows, not five: the same answer names runnersworld, forbes, wired AND reddit as the
 * third-party sources, so a five-row table would contradict the page one scroll below it. Every
 * frame carries all six: the 1024 product preview (296px of content) used to shed the sixth to
 * keep the reading under the table, and the reading is gone.
 *
 * Sizing. The widget body is the query container (container-type: size) because the preview box
 * is only 279px tall at 390 but 447px at 768, so the rows are given height per bucket instead of
 * being squeezed flat. Two consequences drive the structure below:
 *   1. An element cannot query itself, so every container-query-driven declaration lives on a
 *      DESCENDANT — the column template and row height sit on .mkt-cz-table, never on .mkt-cz.
 *   2. Queries resolve against the container's CONTENT box, i.e. the preview's box minus this
 *      body's padding. Measured content boxes (layout px, the page is zoomed .9 above 1000px):
 *      320×279 at 390, 462×296 at 1024, 553×364 at 1440/1920, 662×447 at 768; and on the
 *      homepage SYSTEM stage 320×320 at 390, 662×401 at 768, 539×322 at 1024 and 651×349 at
 *      1440/1920 — hence the 470/400px width and 420/346/308/300/290px height buckets.
 *   3. The vertical rhythm is the same trap: a `gap` on .mkt-cz cannot be re-declared inside a
 *      bucket, so it lives on .mkt-cz-share / .mkt-cz-table instead (see below).
 * The table keeps overflow: hidden as a safety net if a bucket is a few pixels optimistic.
 *
 * One grid trap this widget walked into once: the head and the rows are separate grid containers,
 * and a shared `--cols` string is not a shared layout — an `auto` track resolves per container,
 * so the head's word "MODELE" and a row's marks produced different widths and the header slid
 * right of its data. The models track is therefore an explicit width (--marks) that matches the
 * marks in force. The two legend rows dodge the same trap by being cells of ONE grid
 * (.mkt-cz-leg is display: contents).
 *
 * Class prefix mkt-cz-. The only shared class reused is .mkt-llm (model logo), and it is only
 * ever overridden under .mkt-cz.
 */

@property --cz-n { syntax: '<integer>'; inherits: false; initial-value: 0; }

.mkt-cz {
  container-type: size;
  --ink: #14162B;
  --muted: #656B7E;
  --faint: #9AA0B4;
  --line: #EDEEF2;
  --own: #5B5CE6;
  --third: #F59E0B;
}

/* The vertical rhythm is NOT this element's own `gap`. .mkt-cz is the query container, and an
   element cannot query itself, so a `gap` declared inside one of the @container buckets below
   never applies — three such rules sat here doing nothing, which is why the tall preview never
   got the extra air it was written to have. The rhythm lives on the two children that follow the
   first instead: they ARE descendants, so the buckets reach them. Zeroing the gap has to
   out-specify .mkt-widget.is-compact .mkt-widget-body in marketing.css, hence the long selector. */
.mkt-widget .mkt-widget-body.mkt-cz { gap: 0; }
.mkt-cz-share,
.mkt-cz-table { margin-top: 10px; }

.mkt-cz-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Counters: the integer sits in --to, the real number in the visually hidden span, so a
   blocked stylesheet still reads correctly. */
.mkt-cz-num { --cz-n: var(--to); counter-reset: czn var(--cz-n); }
.mkt-cz-num::after { content: counter(czn); }

/* --- status line + filter chips ----------------------------------------------
 * The chips are the settled state of this row; the spinner, the tick and the three status lines
 * are all invisible at rest and only the sequence brings them in — and takes them away again. */

.mkt-cz-top { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; min-height: 17px; }
.mkt-cz-state { position: relative; width: 14px; height: 14px; flex: 0 0 auto; }
.mkt-cz-spinner {
  position: absolute;
  inset: 0;
  /* border-box, or the 2px ring would make the spinner 18px where the tick that replaces it is
     14px — the swap would jump, and the ring would bleed into the body's padding. */
  box-sizing: border-box;
  border-radius: 50%;
  border: 2px solid #E3E4FA;
  border-top-color: var(--own);
  opacity: 0;
}
.mkt-cz-tick { position: absolute; inset: 0; border-radius: 50%; background: #22C55E; }
.mkt-cz-tick::after {
  content: "";
  position: absolute;
  left: 4.7px;
  top: 3px;
  width: 3.2px;
  height: 6.1px;
  border: solid #FFFFFF;
  border-width: 0 1.6px 1.6px 0;
  transform: rotate(45deg);
}
.mkt-cz-steps { display: grid; flex: 1 1 auto; min-width: 0; font-size: 12.5px; font-weight: 500; color: #262A47; }
.mkt-cz-steps > span {
  grid-area: 1 / 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  opacity: 0;
}
/* The reading the run ends on is the settled state — the one line that wraps instead of being
   clipped, because it is what a viewer without JS or with reduced motion reads. Same rule, slot
   and wording shape as the sibling sentiment, position and leaderboard previews. */
.mkt-cz-steps > .is-result {
  opacity: 1;
  white-space: normal;
  overflow: visible;
  text-wrap: pretty;
}
.mkt-cz-filters { display: flex; gap: 6px; flex: 0 0 auto; margin-left: auto; }
.mkt-cz-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border: 1px solid #E3E5EC;
  border-radius: 999px;
  background: #FFFFFF;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px;
  color: #4C5163;
  white-space: nowrap;
}
.mkt-cz-chip::after {
  content: "";
  width: 4px;
  height: 4px;
  border: solid #9AA0B4;
  border-width: 0 1.2px 1.2px 0;
  transform: translateY(-1px) rotate(45deg);
}

/* --- citation share ----------------------------------------------------------- */

.mkt-cz-share {
  display: flex;
  align-items: center;
  gap: 14px;
  flex: 0 0 auto;
  padding: 9px 13px 10px;
  border: 1px solid var(--line);
  /* Same box as the tiles the sibling widgets put above their tables (.mkt-ql-stat, .mkt-vs-kpi):
     1px --line, 10px radius, white. One band instead of four tiles, but it reads as the same
     furniture. */
  border-radius: 10px;
  background: #FFFFFF;
}
/* Ring + unit. The hole carries the total only; the word sits under the ring, because at this
   size no legible font fits "Cytowania" inside the hole without painting over the stroke. */
.mkt-cz-total { display: flex; flex-direction: column; align-items: center; gap: 3px; flex: 0 0 auto; }
.mkt-cz-donut { position: relative; width: 46px; height: 46px; }
.mkt-cz-donut svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
.mkt-cz-donut circle { fill: none; stroke-width: 5; }
.mkt-cz-donut .is-track { stroke: #EDEEF2; }
/* pathLength 100 turns the arc into percentages: a dash of --len, pushed round by --off. */
.mkt-cz-slice { stroke-dasharray: var(--len) 100; stroke-dashoffset: calc(0 - var(--off)); }
.mkt-cz-slice.is-own { stroke: var(--own); }
.mkt-cz-slice.is-third { stroke: var(--third); }
.mkt-cz-donut-center {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--ink);
}
.mkt-cz-donut-cap {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 6.5px;
  font-style: normal;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--faint);
  white-space: nowrap;
}

/* One grid for both legend rows: name · bar · per cent, columns shared. The band carries no
   title — two named rows beside a ring labelled "Cytowania" need none, and the row that a
   caption would have cost is spent on air instead. */
.mkt-cz-legend {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr) max-content;
  align-items: center;
  column-gap: 10px;
  row-gap: 7px;
  flex: 1 1 auto;
  min-width: 0;
}
.mkt-cz-leg { display: contents; font-size: 11.5px; }
.mkt-cz-leg-name { color: #3A3F55; white-space: nowrap; }
.mkt-cz-leg-pct { font-weight: 600; color: var(--ink); white-space: nowrap; font-variant-numeric: tabular-nums; text-align: right; }
/* The share bar of the app's citation-share card, split into one bar per category. */
.mkt-cz-leg-bar {
  position: relative;
  height: 5px;
  min-width: 0;
  border-radius: 999px;
  background: #EDEEF2;
  overflow: hidden;
}
.mkt-cz-leg-bar::after {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: calc(var(--pct) * 1%);
  border-radius: inherit;
  transform-origin: 0 50%;
}
.mkt-cz-leg.is-own .mkt-cz-leg-bar::after { background: var(--own); }
.mkt-cz-leg.is-third .mkt-cz-leg-bar::after { background: var(--third); }

/* --- source URL table ----------------------------------------------------------
 * The column template and row height live here, not on .mkt-cz: .mkt-cz is the query container
 * and an element cannot query itself.
 *
 * The table GROWS (flex: 1 1 auto) and so do its rows: whatever height a bucket leaves over is
 * spent on the row pitch, not banked into one empty band under the last row. It used to be
 * flex: 0 1 auto, which sized the table to exactly six times --row-h and pushed every spare
 * pixel into the gap under the last row — 65px of blank white on the phone home stage, 47px at
 * 1024, ~30px on the desktop previews. --row-h is now the FLOOR of the pitch; the ceiling is
 * +16px, a guard rail so an unexpectedly tall container cannot stretch six rows into a ladder. */

.mkt-cz-table {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  /* url · mentions · models. The models track is explicit: nine marks of --mark plus eight gaps,
     so the header sits over its own data instead of over an `auto` track that each grid
     container resolves for itself. */
  --mark: 12px;
  --marks: calc(9 * var(--mark) + 8 * 3px);
  --cols: minmax(0, 1fr) 84px var(--marks);
  --gap: 12px;
  --row-h: 25px;
  /* How many rows are on screen and how many rules run between them: the skeleton draws its
     pitch from these, so it keeps lining up with rows that have grown past --row-h. */
  --rows: 6;
  --rows-rules: 5;
}
.mkt-cz-head,
.mkt-cz-row {
  display: grid;
  grid-template-columns: var(--cols);
  align-items: center;
  column-gap: var(--gap);
}
.mkt-cz-head {
  flex: 0 0 auto;
  padding: 0 6px 5px;
  border-bottom: 1px solid var(--line);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--faint);
  white-space: nowrap;
}
.mkt-cz-head .is-m { text-align: center; }
.mkt-cz-head .is-n,
.mkt-cz-head .is-m { overflow: hidden; text-overflow: ellipsis; }

/* The rows take the table's spare height between them, evenly. */
.mkt-cz-rows { position: relative; display: flex; flex-direction: column; flex: 1 1 auto; }
/* The table's rules, drawn at the row pitch and shown only while the rows are landing: the
   frame is a table from the first beat instead of an empty box. Invisible in the final frame
   (opacity 0 here, raised only by the sequence), so the finished rules stay the rows' own.
   The pitch is read off the rows themselves, in per cent, not off --row-h: the rows grow past
   --row-h whenever the bucket has height to spare, and a fixed-pitch skeleton would have drifted
   away from the rows it is standing in for. One row short at the bottom, so no rule is drawn
   under the last row; one tile per gap between rows, each carrying its rule on its own edge. */
.mkt-cz-rows::before {
  content: "";
  position: absolute;
  inset: 0 0 calc(100% / var(--rows)) 0;
  opacity: 0;
  pointer-events: none;
  background-image: linear-gradient(to bottom, transparent calc(100% - 1px), #F1F2F7 calc(100% - 1px));
  background-size: 100% calc(100% / var(--rows-rules));
}
.mkt-cz-row {
  position: relative;
  /* --row-h is the floor, not the height: spare height in the bucket is shared out over the
     rows as air. The ceiling keeps that air from turning into a ladder. */
  flex: 1 1 var(--row-h);
  min-height: var(--row-h);
  max-height: calc(var(--row-h) + 16px);
  padding: 0 6px;
  border-radius: 6px;
  font-size: 11.5px;
  color: var(--ink);
}
.mkt-cz-row + .mkt-cz-row { box-shadow: inset 0 1px 0 #F4F5F8; }
/* Your own pages, the thing this view is read for: a faint lilac band down the table. */
.mkt-cz-row.is-own { background: #F4F5FE; box-shadow: none; }
.mkt-cz-row.is-own + .mkt-cz-row { box-shadow: none; }

/* No leading colour dot: own-vs-third is already said by the row's lilac band, by the host name's
   colour and by the bar's colour, and a fourth copy only pushed every host 10px right of the "URL"
   header it belongs under. Dropping it lines the column up with its own heading. */
.mkt-cz-row .is-url { display: flex; align-items: baseline; gap: 5px; min-width: 0; }
.mkt-cz-row .is-url b {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
}
.mkt-cz-row.is-own .is-url b { color: #4B4CD8; }
.mkt-cz-path {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: var(--muted);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Mentions: the bar carries the comparison, the number the exact value. */
.mkt-cz-row .is-n { display: flex; align-items: center; gap: 7px; min-width: 0; }
.mkt-cz-bar {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  height: 4px;
  border-radius: 999px;
  background: #EDEEF2;
  overflow: hidden;
}
.mkt-cz-bar::after {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: calc(var(--w) * 1%);
  border-radius: inherit;
  background: var(--third);
  transform-origin: 0 50%;
}
.mkt-cz-row.is-own .mkt-cz-bar::after { background: var(--own); }
.mkt-cz-n {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  font-weight: 600;
  color: var(--ink);
  flex: 0 0 auto;
  min-width: 13px;
  text-align: right;
}

/* The nine per-model count columns of the app's table, drawn as nine marks: lit = this model
   cites the URL. Reading down the column shows which models lean on which sources. */
.mkt-cz-row .is-m { display: flex; align-items: center; justify-content: center; gap: 3px; }
.mkt-cz-mark { display: block; flex: 0 0 auto; line-height: 0; }
.mkt-cz .mkt-cz-mark .mkt-llm { width: var(--mark); height: var(--mark); }
.mkt-cz-mark.is-off { opacity: 0.16; filter: grayscale(1); }

/* Tall previews (the single-column tablet preview, 662×447 content): more air, taller rows. */
@container (min-height: 420px) {
  .mkt-cz-share,
  .mkt-cz-table { margin-top: 12px; }
  .mkt-cz-table {
    --row-h: 40px;
    --mark: 14px;
    --cols: minmax(0, 1fr) 104px var(--marks);
  }
  .mkt-cz-steps { font-size: 13px; }
  .mkt-cz-share { padding: 11px 15px 12px; gap: 16px; }
  .mkt-cz-total { gap: 4px; }
  .mkt-cz-donut { width: 60px; height: 60px; }
  .mkt-cz-donut-center { font-size: 17px; }
  .mkt-cz-donut-cap { font-size: 7.5px; }
  .mkt-cz-legend { column-gap: 12px; row-gap: 10px; }
  .mkt-cz-leg { font-size: 12.5px; }
  .mkt-cz-leg-bar { height: 6px; }
  .mkt-cz-row { font-size: 12px; }
  .mkt-cz-row .is-url b, .mkt-cz-path, .mkt-cz-n { font-size: 11.5px; }
  .mkt-cz-head { font-size: 8.5px; padding-bottom: 6px; }
}

/* The desktop previews (558×368 at 1440/1920) and the homepage SYSTEM stage (656×353). */
@container (min-height: 346px) and (max-height: 419px) {
  .mkt-cz-table { --row-h: 30px; }
  .mkt-cz-donut { width: 52px; height: 52px; }
  .mkt-cz-donut-center { font-size: 15px; }
  .mkt-cz-donut-cap { font-size: 7px; }
  .mkt-cz-legend { row-gap: 8px; }
  .mkt-cz-leg { font-size: 12px; }
}

/* The shortest preview (320×279 at 390): tighter pitch so all six rows still fit. */
@container (max-height: 290px) {
  .mkt-cz-share,
  .mkt-cz-table { margin-top: 8px; }
  /* 8px of rhythm instead of 10 plus the slack that used to sit under the last row buys two more
     pixels per row — the phone frame is the only one where the pitch was genuinely tight. */
  .mkt-cz-table { --row-h: 26px; }
  .mkt-cz-share { padding: 7px 10px 8px; }
}

/* Narrow previews (the phone column and the 1024 two-column grid, both under 470px of content):
   smaller marks and type, and the mentions track gives its width back to the URL — the URL is
   what the table is read for at this size. */
@container (max-width: 470px) {
  .mkt-cz-table {
    --mark: 10px;
    --marks: calc(9 * var(--mark) + 8 * 2px);
    --cols: minmax(0, 1fr) 54px var(--marks);
    --gap: 7px;
  }
  .mkt-cz-steps { font-size: 11.5px; }
  .mkt-cz-share { gap: 11px; }
  .mkt-cz-total { gap: 2px; }
  .mkt-cz-donut { width: 42px; height: 42px; }
  .mkt-cz-donut-center { font-size: 12.5px; }
  .mkt-cz-donut-cap { font-size: 6px; }
  .mkt-cz-legend { column-gap: 8px; row-gap: 5px; }
  .mkt-cz-leg { font-size: 10.5px; }
  .mkt-cz-leg-bar { height: 4px; }
  .mkt-cz-row { font-size: 10.5px; padding: 0 5px; }
  /* 8px mono put "WZMIANKI" one pixel over its own track and left it reading "WZMIAN…"; the
     sibling query-list head drops to the same 7.5px at this width. */
  .mkt-cz-head { font-size: 7.5px; letter-spacing: 0.04em; padding: 0 5px 4px; }
  .mkt-cz-row .is-url b, .mkt-cz-path { font-size: 10px; }
  .mkt-cz-n { font-size: 10px; min-width: 11px; }
  .mkt-cz-row .is-m { gap: 2px; }
}

/* The phone preview (320px of content) cannot hold the status line next to the chips, and the
   chips win: they are permanent scope, the status line is a 3-second narration. The width the
   status line gives up goes to the URL column, which is what is actually read here: at 46px the
   mentions track still holds the bar and a two-digit count. */
@container (max-width: 400px) {
  .mkt-cz-state,
  .mkt-cz-steps { display: none; }
  .mkt-cz-filters { margin-left: 0; }
  /* And one chip, not two — the same half the sibling leaderboard and query-list keep at this
     width, for the same reason: the period is what the 128 and the six counts were measured over
     and nothing else in the frame says it, while "all LLMs" is the default scope of a Monitoring
     view. Two chips here were the only place in the group where the row carried both. */
  .mkt-cz-filters .mkt-cz-chip:first-child { display: none; }
  /* The phone column pays for its rhythm out of the same 320px the rows need: 8px between the
     three bands instead of 10 is the difference between six rows and six rows with the last one
     shaved. */
  .mkt-cz-share,
  .mkt-cz-table { margin-top: 8px; }
  /* Nine marks took 36px more than six did, and the URL is what this width is read for: 9px
     marks on 1px gaps (89px), 5px column gaps, a 42px mentions track (the "WZMIANKI" header's own
     width) and 9.5px mono give the paths back the width they had with six marks — at 390 every
     path is spelled out again (at 360 the longest ones were cut with six marks too). */
  .mkt-cz-table {
    --mark: 9px;
    --marks: calc(9 * var(--mark) + 8 * 1px);
    --cols: minmax(0, 1fr) 42px var(--marks);
    --gap: 5px;
  }
  .mkt-cz-row .is-m { gap: 1px; }
  .mkt-cz-row .is-url b, .mkt-cz-path { font-size: 9.5px; }
  .mkt-cz-row .is-n { gap: 5px; }
  /* At 0.04em the header measured 42.4px (tracking counts after the last letter too) and read
     "WZMIAN…" in its 42px track; 0.02em brings it to 41.2 and spells it out. */
  .mkt-cz-head { letter-spacing: 0.02em; }
}

/* The middle heights (462×296 at 1024, 539×322 and 320×320 on the homepage stage). This sits
   after the width buckets on purpose — where both match, the height is the binding one. */
@container (min-height: 291px) and (max-height: 345px) {
  .mkt-cz-table { --row-h: 26px; }
}

/* --- sequence -------------------------------------------------------------------- */

.mkt-cz.is-armed .mkt-cz-chip { animation: cz-rise 0.3s ease calc(var(--i) * 0.08s) backwards; }

.mkt-cz.is-armed .mkt-cz-share { animation: cz-fade 0.3s ease 0.02s backwards; }
.mkt-cz.is-armed .mkt-cz-slice { animation: cz-slice 0.55s cubic-bezier(0.22, 1, 0.36, 1) calc(0.12s + var(--k) * 0.13s) backwards; }
.mkt-cz.is-armed .mkt-cz-donut-center { animation: cz-count 0.85s ease-out 0.18s both; }
.mkt-cz.is-armed .mkt-cz-donut-cap { animation: cz-fade 0.35s ease 0.5s backwards; }
/* The two rows are halves of one whole, so the WHOLE band runs on one schedule: the rows, the
   bars and the per cents all start at 0.36s. A 0.12s row stagger used to leave "Domena marki ·
   0 %" alone on screen for two frames with no "Strony trzecie" under it and an empty bar beside
   it, which reads as "the brand domain has no citations" — the split that does not add up, moved
   from the counters onto the rows. 0.36s rather than the counters' old 0.42s: until the rows
   arrive the band is a white box with nothing in it but the ring, and it should not sit that way
   a frame longer than it must. */
.mkt-cz.is-armed .mkt-cz-leg > * { animation: cz-rise 0.32s ease 0.36s backwards; }
.mkt-cz.is-armed .mkt-cz-leg-bar::after { animation: cz-bar 0.55s cubic-bezier(0.22, 1, 0.36, 1) 0.36s backwards; }
.mkt-cz.is-armed .mkt-cz-leg-pct .mkt-cz-num { animation: cz-count 0.62s ease-out 0.36s both; }

.mkt-cz.is-armed .mkt-cz-head { animation: cz-fade 0.3s ease 0.08s backwards; }
/* The empty table is drawn before anything lands in it, so the frame is a view from the first
   beat instead of an empty box, and it is gone by the time the last row arrives. */
.mkt-cz.is-armed .mkt-cz-rows::before { animation: cz-skeleton 1.6s ease 0.05s both; }
/* The rows land in order — the heart of the sequence. */
.mkt-cz.is-armed .mkt-cz-row { animation: cz-row 0.36s cubic-bezier(0.22, 1, 0.36, 1) calc(0.5s + var(--i) * 0.13s) backwards; }
.mkt-cz.is-armed .mkt-cz-bar::after { animation: cz-bar 0.44s cubic-bezier(0.22, 1, 0.36, 1) calc(0.56s + var(--i) * 0.13s) backwards; }
/* Row counts start at 1, not 0: a source URL with no citations is not a row this table has. */
.mkt-cz.is-armed .mkt-cz-n { animation: cz-count-row 0.5s ease-out calc(0.56s + var(--i) * 0.13s) both; }
.mkt-cz.is-armed .mkt-cz-mark { animation: cz-pop 0.28s cubic-bezier(0.2, 0.9, 0.3, 1.3) calc(0.62s + var(--i) * 0.13s + var(--m) * 0.025s) backwards; }

/* The status line hands over cleanly and then STAYS on the result. Every handover is SEQUENTIAL,
   never a cross-fade: the two narration windows are laid end to end (0–0.88 · 0.88–1.70) so two
   readings of the slot are never legible at once — the same rule the sibling query-list and
   leaderboard status lines follow. They used to overlap by 0.07s, which painted two different
   Polish sentences on top of each other, 3px apart, at both handovers. The tick pops on the frame
   the last line starts, and neither clears: the line the run ends on is not "monitoring updated"
   but what the table underneath it says, so there is no stale alert to get rid of — and the left
   half of the row is not left empty under the chips, which is what the fade used to do (measured
   at 1440: the 389x17 text column blank, the chips 403px to its right). Both rest visible, so
   `backwards` fill holds only the first frame and the animation ends exactly where the no-JS
   frame already is. */
.mkt-cz.is-armed .mkt-cz-spinner { animation: cz-spin 0.7s linear infinite, cz-hold 1.7s linear both; }
.mkt-cz.is-armed .mkt-cz-tick { animation: cz-pop 0.34s cubic-bezier(0.2, 0.9, 0.3, 1.3) 1.7s backwards; }
.mkt-cz.is-armed .mkt-cz-steps > .is-collect { animation: cz-window 0.88s ease 0s both; }
.mkt-cz.is-armed .mkt-cz-steps > .is-group { animation: cz-window 0.82s ease 0.88s both; }
.mkt-cz.is-armed .mkt-cz-steps > .is-result { animation: cz-rise 0.3s ease 1.7s backwards; }

/* Waiting for marketing.js to mark the widget live: hold the first frame. */
.mkt-widget:not(.is-live) .mkt-cz.is-armed *,
.mkt-widget:not(.is-live) .mkt-cz.is-armed *::after,
.mkt-widget:not(.is-live) .mkt-cz.is-armed *::before { animation-play-state: paused !important; }

@media (prefers-reduced-motion: reduce) {
  .mkt-cz *,
  .mkt-cz *::after,
  .mkt-cz *::before { animation: none !important; }
}

@keyframes cz-fade { from { opacity: 0; } }
@keyframes cz-rise { from { opacity: 0; translate: 0 4px; } }
@keyframes cz-pop { from { opacity: 0; scale: 0.6; } }
@keyframes cz-row { from { opacity: 0; translate: 0 6px; } }
@keyframes cz-window { 0% { opacity: 0; translate: 0 3px; } 12%, 88% { opacity: 1; translate: 0 0; } 100% { opacity: 0; translate: 0 -3px; } }
@keyframes cz-bar { from { transform: scaleX(0); } }
@keyframes cz-slice { from { stroke-dasharray: 0 100; } }
@keyframes cz-count { from { --cz-n: 0; } }
@keyframes cz-count-row { from { --cz-n: 1; } }
@keyframes cz-skeleton { 0% { opacity: 0; } 10%, 66% { opacity: 1; } 100% { opacity: 0; } }
@keyframes cz-spin { to { rotate: 360deg; } }
@keyframes cz-hold { 0%, 96% { opacity: 1; } 100% { opacity: 0; } }

/* === content-demo ========================================================== */
/*
 * Product page preview — "Działaj" → Tworzenie treści (widgets/v2/content-demo.blade.php).
 *
 * Every rule below describes the FINISHED view: the recommended articles sorted by impact, the
 * top one opened with the four metrics the app's recommendation modal shows (priority, strategy,
 * rank position, current score) plus the AI query it moves and the ContentInAI hand-off, and the
 * four remaining rows, each with the reason it is on the list.
 * The sequence that leads there is a set of keyframes whose early frames hold the earlier states.
 * They apply only once the widget is armed by its inline script, sit paused until marketing.js
 * marks the widget live, and restart by themselves each time the accordion shows the widget again
 * (display: none cancels CSS animations).
 *
 * ContentInAI — the separate content product — is named once, in its own orange (#F8A32A, the
 * colour src/lib/contentinai.ts defines and the home / product ContentInAI sections use): the
 * hand-off at the foot of the picked card ("Pisz w ContentInAI →", the button the app puts on
 * every recommendation). The subject-less "integruje się z ContentInAI" tag that used to sit
 * beside the list title is gone — it only read as a sentence with the "BrandinAI" chip the app's
 * hero has in front of it, and there is no room for all three at 390 or 1024.
 *
 * Timeline (s): 0–0.5 the list title · 0.28 the top proposal lands, its values still under a
 *               loading skeleton · 0.5–1.1 the four other proposals land in order with their
 *               reason and impact · 2.0–2.7 the top one is picked: the card lights up and each
 *               skeleton resolves into the value it was holding — priority, strategy, position,
 *               score, description and query · 2.72 the ContentInAI hand-off appears on the
 *               picked topic, and the frame is settled. Nothing opens or closes above the list,
 *               so it starts and ends in the same place.
 *
 * Every skeleton sits on a VALUE slot, never across a label: the mono labels stay readable for the
 * whole sequence, which is what the app's own loading card looks like.
 *
 * Layout note: .mkt-cd is the query container, so everything it lays out sits in .mkt-cd-stage —
 * an @container rule styles a container's DESCENDANTS, never the container element itself, so
 * column tracks, gaps and the scale unit below have to live one level in to be overridable per
 * preview width. (cqw inside .mkt-cd itself would resolve against the container ABOVE it.)
 *
 * Class prefix mkt-cd-. The only shared classes reused are .mkt-prio (impact chip) and .mkt-pill
 * (position pill), and both are only ever overridden under .mkt-cd.
 *
 * ---------------------------------------------------------------------------------------------
 * ONE UNIT, NOT THREE STEPS (why every length below is a multiple of --u)
 *
 * The "Działaj" preview is a SQUARE (.mkt-how-preview--act, aspect-ratio 1/1), so the frame is
 * exactly as tall as it is wide, and the widget's body padding takes 39 px off the height. The
 * widget used to be sized in fixed px at three container steps (≤380 / ≤470 / ≥620). Fixed type
 * in a frame whose height grows with its width means the content stops growing while the frame
 * keeps growing, and the difference has to go somewhere. Measured before this round, in the
 * square frame, PL, as (frame height − what the content needs):
 *       cq 298 →   +8    cq 388 →  −74    cq 538 →  +50    cq 662 → +152    cq 853 → +344
 * Both failures on this widget's ticket are the two ends of that one line:
 *   · the PLUS end was the "dziura". The round before this one handed it to the card
 *     (flex-grow 2 + space-between), which turned one band under the list into three chasms
 *     inside the picked-proposal card: 44,7 px each at 768, 74,1 at 900, 87,2 at 959 — measured
 *     largest empty band in the widget 107 → 115 px across 800–958, worse than the band it
 *     replaced. Nobody can spread 344 px of nothing between four parts and have it read.
 *   · the MINUS end was the text-on-text overlap. Below cq 380 the widget sheds the description,
 *     the column head, the reading and the fifth proposal, and what is left fits a square frame.
 *     From cq 381 the ≤470 step puts all four back at the same type size — into a frame that is
 *     only 5 px taller. The list then had to give up 88 px it did not have, and the last row's
 *     text landed on the reading under it. In viewport terms that is 455–494 px and 965–999 px
 *     (two windows because the "Jak to działa" column goes two-up at 960 and the container width
 *     falls back into the same band); the ticket saw the second one.
 *
 * So the sizes are not stepped any more. One unit, --u, carries every length in the widget, and
 * it is a straight line in the container width: --u = 0,95 × (cq − 39) / C, where C is what the
 * widget needs in units of --u (39,5 with the full set of parts, 26,6 with the phone set) and the
 * 0,95 leaves the rows a few px of slack to breathe on instead of the frame keeping it. In a
 * square frame that makes what the content needs track the frame at every width — no step to fall
 * between, nothing left over to hide and nothing to squeeze out. Written as a clamp of cqw it is
 * the ramp below. At cq 550 (the old middle step) every value is the value it was before this
 * round, to the pixel: --u = 11,5px is the old base, and every multiple here is <old px> / 11,5.
 *
 * The widget's second home, the ContentInAI panel on /produkt, is NOT square — that panel is as
 * tall as the tallest slide on its stage — so there the same ramp renders the same view at the
 * size its column can carry, the widget is stretched to the panel (home-v2.css,
 * .hp-ciap-widget .mkt-widget) and the rows take up the difference. hv-contentinai.css block 6h
 * keeps its own say over what the 900–999 px band shows and how tall its rows may grow.
 */

@property --cd-n { syntax: '<integer>'; inherits: false; initial-value: 0; }

.mkt-cd {
  container-type: inline-size;
  gap: 0;
  --ink: #14162B;
  --muted: #656B7E;
  --faint: #9AA0B4;
  --line: #EDEEF2;
  --brand: #5B5CE6;
  --skel: #F1F2F6;
  /* ContentInAI's own orange, from src/lib/contentinai.ts (CONTENTINAI_ORANGE). */
  --cia: #F8A32A;
}

.mkt-cd-stage {
  /* The scale unit. 2,4cqw − 0,94px is 0,95 × (cq − 39) / 39,5 written as a container length;
     the floor keeps the narrowest full-set frame readable, the ceiling stops the widest square
     frame this widget is ever given (cq 853 at 959 px of viewport) from overshooting it. */
  --u: clamp(7px, calc(2.4cqw - 0.94px), 20.6px);
  display: flex;
  flex-direction: column;
  gap: calc(0.957 * var(--u));
  flex: 1 1 auto;
  min-height: 0;
  /* topic · impact — the two columns the app's recommendation grid actually has. */
  --cols: minmax(0, 1fr) calc(5.043 * var(--u));
  --gap: calc(0.87 * var(--u));
}

.mkt-cd-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Counters: the integer sits in --to, the real number in the visually hidden span, so a blocked
   stylesheet still reads correctly. */
.mkt-cd-num { --cd-n: var(--to); counter-reset: cdn var(--cd-n); }
.mkt-cd-num::after { content: counter(cdn); }

.mkt-cd-cap {
  font-family: 'IBM Plex Mono', monospace;
  font-size: calc(0.783 * var(--u));
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}

/* --- no status line ------------------------------------------------------------- */

/* There is none. The card under the header already shows the wait a narration row would have
   spelled out — four value slots and a description sitting under skeletons until the pick
   resolves them — and the row could not be had for free: it opened for the run and closed again
   at the end (a held-open row leaves an empty strip on the settled frame), which lifted the
   whole list by a row's height on the last beat. The list now starts where it finishes.
   Taking the row out freed its height and the stage gap that went with it, and that room is
   gone, not moved: with --u tracking the frame, the parts that are left grow into it in
   proportion instead of one of them being handed the whole surplus as blank space. */

/* --- list header ----------------------------------------------------------------- */

.mkt-cd-head { flex: 0 0 auto; min-width: 0; }
.mkt-cd-head > strong {
  display: block;
  font-size: calc(1.13 * var(--u));
  font-weight: 600;
  color: var(--ink);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- the picked proposal --------------------------------------------------------- */

/* The card is exactly as tall as its four parts, at every width: flex 0 0 auto, no
   justify-content. It grew for one round (flex 2, space-between) to soak up the frame's surplus,
   and that is what put three 45–87 px chasms between its title, its metrics, its description and
   its foot. The surplus is not the card's to hold: --u already makes the card grow with the
   frame, and whatever few px are left over after that go to the rows, which have somewhere to
   put them (see .mkt-cd-row). */
.mkt-cd-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: calc(0.783 * var(--u));
  flex: 0 0 auto;
  padding: calc(0.957 * var(--u)) calc(1.13 * var(--u)) calc(1.043 * var(--u));
  border: 1px solid #C7C8F6;
  border-radius: calc(1.13 * var(--u));
  background: #FCFCFE;
  box-shadow: 0 10px 22px -18px rgba(20, 22, 43, 0.55);
}
.mkt-cd-card-title {
  margin: 0;
  min-width: 0;
  font-size: calc(1.174 * var(--u));
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-wrap: pretty;
}

/* The four columns of the app's recommendation modal — priority, strategy, position, current
   score. Three of them are exactly as wide as they need to be (max-content, so the mono label or
   the chip under it decides, in Polish and in English alike) and the strategy takes everything
   that is left: the row is read across the whole card instead of trailing off into white. */
.mkt-cd-metrics {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr) max-content max-content;
  gap: calc(0.522 * var(--u)) calc(0.87 * var(--u));
  margin: 0;
}
.mkt-cd-metrics > div { display: flex; flex-direction: column; gap: calc(0.348 * var(--u)); min-width: 0; }
.mkt-cd-metrics dt {
  font-family: 'IBM Plex Mono', monospace;
  font-size: calc(0.739 * var(--u));
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--faint);
  white-space: nowrap;
}
.mkt-cd-metrics dd { position: relative; margin: 0; display: flex; align-items: center; min-height: calc(2.261 * var(--u)); }
/* The value slots load: a skeleton bar sits in each until the pick resolves it. */
.mkt-cd-metrics dd::before {
  content: "";
  position: absolute;
  left: 0;
  top: calc(0.435 * var(--u));
  width: calc(3.826 * var(--u));
  height: calc(1.304 * var(--u));
  border-radius: calc(0.522 * var(--u));
  background: var(--skel);
  opacity: 0;
}
.mkt-cd .mkt-prio { font-size: calc(0.826 * var(--u)); padding: calc(0.261 * var(--u)) calc(0.609 * var(--u)); }
.mkt-cd .mkt-pill {
  width: calc(2.261 * var(--u));
  height: calc(2.261 * var(--u));
  border-radius: calc(0.609 * var(--u));
  font-size: calc(1.043 * var(--u));
}

/* The strategy value is plain text in the modal too, and it wraps rather than ellipsising: the
   settled frame never carries a half-written strategy name. */
.mkt-cd-strat {
  font-size: var(--u);
  font-weight: 600;
  line-height: 1.25;
  color: var(--ink);
  text-wrap: pretty;
}
/* Its loading slot is as wide as the value it holds. */
.mkt-cd-metrics .is-strategy dd::before { width: auto; right: 8%; }

.mkt-cd-ring { position: relative; display: block; width: calc(2.261 * var(--u)); height: calc(2.261 * var(--u)); }
.mkt-cd-ring svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
.mkt-cd-ring circle { fill: none; stroke-width: 5; }
.mkt-cd-ring .is-track { stroke: #EDEEF2; }
.mkt-cd-ring .is-value { stroke: var(--ring); stroke-linecap: round; stroke-dasharray: 100; stroke-dashoffset: calc(100 - var(--score)); }
.mkt-cd-ring .mkt-cd-num {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: calc(0.87 * var(--u));
  font-weight: 600;
  color: var(--ink);
}

/* Two of this widget’s parts are <p> — the description in the card and the query block under it
   (a third, the reading under the list, has since gone) — and marketing.css’s shared mobile
   typography carries `p { font-size: 14px !important }` below a 640 px viewport. Below that line
   they stopped scaling with --u and jumped to a flat 14px, whatever the container was: measured in
   the square “Działaj” preview at 455 px of viewport, description 14px / 61 px tall and reading
   14px / 59 px tall against the 8,3px / 24 px and 7,6px / 21 px the same container width renders
   above 640 (965 px of viewport, cq 380). The card and the reading took 78 px the frame did not
   have, and the list — the one flexible part — was squeezed 33 px past its own box, so its last
   two rows printed on top of the reading: the text-on-text overlap at 455-470 px (PL). It also put
   a step in the type at the 640/641 seam at one and the same container width (description 14px →
   12px). The widget’s own sizes take it back — both declarations are !important, and a class beats
   a type selector. */
.mkt-cd-desc {
  position: relative;
  margin: 0;
  font-size: var(--u) !important;
  line-height: 1.45;
  color: #4C5163;
  text-wrap: pretty;
}
.mkt-cd-desc span { display: block; }
/* The description is exactly as tall as the description is — no min-height. It used to reserve
   two lines (2,783 × --u) so the two skeleton bars below always had a line each. The English
   description is one line wherever it fits one (cq 664-684, 694-704, 734 and 814 in the square
   preview, i.e. viewport 770-790, 800-810, 840 and 920), and there the reservation held the card
   open over an empty line: measured tallest empty band inside the widget 54,2 px at 920 against
   the 37,8 px the same frame reads at 925 with a two-line description. That is the “dziura” again,
   one card part further in. The bars keep their two lines without it: the first is anchored to the
   top of the slot and the second to its BOTTOM, so a two-line description gets two bars and a
   one-line description gets one — they merge into a single full-width bar exactly a line tall. */
.mkt-cd-desc::before,
.mkt-cd-desc::after {
  content: "";
  position: absolute;
  left: 0;
  height: calc(0.783 * var(--u));
  border-radius: calc(0.435 * var(--u));
  background: var(--skel);
  opacity: 0;
}
/* The two insets are (1,45 − 0,783) / 2: a one-line slot is 1,45 × --u tall (its line-height),
   so on one line the two bars land on exactly the same 0,783 × --u strip and merge without a step. */
.mkt-cd-desc::before { top: calc(0.334 * var(--u)); right: 0; }
.mkt-cd-desc::after { bottom: calc(0.334 * var(--u)); right: 32%; }

/* The foot of the app's recommendation modal: the query the article moves, and the button that
   opens the editor in ContentInAI carrying this project and this recommendation. */
.mkt-cd-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: calc(0.87 * var(--u));
  min-width: 0;
  padding-top: calc(0.696 * var(--u));
  border-top: 1px dashed #E3E5EC;
}
/* Label over value, like the four metrics above it — and it leaves the value the whole width of
   the foot minus the hand-off, so it is never cut short in the settled frame. */
.mkt-cd-query {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: calc(0.174 * var(--u));
  margin: 0;
  /* No text of its own (label and value carry their own sizes), but it must not inherit the
     mobile 14px either: its line box would set the gap between them. */
  font-size: var(--u) !important;
  flex: 1 1 auto;
  min-width: 0;
}
/* The query the article moves wraps rather than ellipsising: this value is the settled frame in
   the ContentInAI section too, where the panel is narrower than any accordion preview. */
.mkt-cd-query b {
  display: block;
  min-width: 0;
  font-size: var(--u);
  font-weight: 600;
  line-height: 1.3;
  color: #4B4CD8;
  text-wrap: pretty;
}
/* The query's loading skeleton belongs to the VALUE slot, so the "Wpływ na zapytanie AI" label
   beside it is readable from the moment it fades in — the same way the three metric labels are. */
.mkt-cd-query-val { position: relative; align-self: stretch; min-width: 0; }
.mkt-cd-query-val::before {
  content: "";
  position: absolute;
  left: 0;
  right: 26%;
  top: calc(0.174 * var(--u));
  height: calc(0.957 * var(--u));
  border-radius: calc(0.435 * var(--u));
  background: var(--skel);
  opacity: 0;
}
.mkt-cd-write {
  flex: 0 0 auto;
  padding: calc(0.348 * var(--u)) calc(0.783 * var(--u));
  border-radius: calc(0.609 * var(--u));
  background: var(--cia);
  color: #FFFFFF;
  font-size: calc(0.87 * var(--u));
  font-weight: 600;
  line-height: 1.4;
  white-space: nowrap;
  box-shadow: 0 8px 16px -12px rgba(217, 137, 20, 0.95);
}

/* --- the rest of the list --------------------------------------------------------- */

.mkt-cd-list { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.mkt-cd-colhead,
.mkt-cd-row {
  display: grid;
  grid-template-columns: var(--cols);
  align-items: center;
  column-gap: var(--gap);
}
.mkt-cd-colhead {
  flex: 0 0 auto;
  padding: 0 calc(0.348 * var(--u)) calc(0.435 * var(--u));
  border-bottom: 1px solid var(--line);
  font-family: 'IBM Plex Mono', monospace;
  font-size: calc(0.739 * var(--u));
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--faint);
  white-space: nowrap;
}
.mkt-cd-colhead .is-impact { overflow: hidden; text-overflow: ellipsis; }

/* The rows are the widget's one flexible part, and the only one that can take a surplus without
   turning it into a hole: a row that grows puts the room between its two lines of type and its
   neighbours, which reads as air, not as a gap. They take whatever --u has not already spent —
   by construction a few per cent of the square frame, more in the ContentInAI panel — and stop at
   a cap of 6,087 × --u: the two-line row plus its padding, plus one more line of type and the gap
   that separates the two:
       --u × (2 × 1,25 + 0,174 + 0,87 × 1,3 + 2 × 0,435) = 4,675 × --u   the row itself
       + 1,25 × --u (a line) + 0,174 × --u (its gap)        = 6,10 × --u   →  cap 6,087 × --u
   (70 / 11,5 in the scale this file is written in; hv-contentinai.css block 6h raises it to 80px
   for the 900–999 px band of the ContentInAI panel, where the frame is taller than it is wide).
   The cap used to be 5,043 × --u (58 / 11,5), and that was the last place the “dziura” survived:
   the ContentInAI panel is the one frame whose height is not the widget’s to choose — it is as
   tall as the tallest slide on its stage — so at 1000–1040 px of viewport the English list had
   32,9 px more room than four capped rows could take, and all of it pooled under the last row
   (measured under-list void: EN 21,9 px at 1000, 29,7 px at 1005, 27,5 at 1010, 24,3 at 1015,
   22,1 at 1020, 18,9 at 1025, 16,7 at 1030, 12,2 at 1040; PL 10,5 / 8,1 / 5,7 / 2,4). Raising
   the cap hands that room to the rows, which is the only part of the widget that can hold it as
   air — and it changes nothing at any width where the cap was not already the binding
   constraint, because flex-grow only grows into free space. */
.mkt-cd-row {
  flex: 1 1 auto;
  min-height: calc(2.957 * var(--u));
  max-height: calc(6.087 * var(--u));
  padding: calc(0.435 * var(--u)) calc(0.348 * var(--u));
  border-radius: calc(0.609 * var(--u));
}
.mkt-cd-row + .mkt-cd-row { box-shadow: inset 0 1px 0 #F4F5F8; }
.mkt-cd-row .is-topic { display: flex; flex-direction: column; gap: calc(0.174 * var(--u)); min-width: 0; }
/* The title IS the row — it is the topic the recommendation is about — so it wraps instead of
   being cut, the same rule the article title in the ContentInAI section on home follows ("never
   truncated, always wraps"). It used to be nowrap + ellipsis, and wherever the topic column got
   narrow the row ended in a "…": in the 900–999 px band of the ContentInAI panel on /produkt
   ("Buty do biegania dla pronatorów: co dor…", 233 px slot for 285 px of title) and in the 390 px
   phone frame. The reason line under it keeps the ellipsis: it is a gloss, not the thing named,
   and a second line of it would push the title's own second line out of the row. */
.mkt-cd-row-title {
  font-size: var(--u);
  font-weight: 600;
  line-height: 1.25;
  color: var(--ink);
  min-width: 0;
  text-wrap: pretty;
}
.mkt-cd-row-why {
  font-size: calc(0.87 * var(--u));
  line-height: 1.3;
  color: var(--muted);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mkt-cd-row .is-impact { display: flex; }

/* The narrowest frames keep the picked proposal whole — title, the four metrics, the query it
   moves and the ContentInAI hand-off — plus three more rows; only the long description, the
   column header and the fifth row aside so nothing is cut off. With four parts fewer the
   widget needs 26,6 × --u instead of 39,5, so the ramp is the steeper one: at 390 px of viewport
   (cq 320, the 350 px phone square) that is 10,0px. It was 25,8 (10,4px at 390), and 25,8 was
   half a line too generous: the phone rows carry two lines of type each, and wherever the second
   one had to wrap the list came up short and the rows printed past their own boxes — measured
   row overflow, before → after, PL 3 px at 395, 4 at 400, 3 at 405, 5 at 410 and 8 at 450, EN
   3-5 px at every step from 380 to 450 → 0 px everywhere in both. Nothing collided (the room came
   out of the space between the rows, not out of the type), but a list whose rows touch is the
   same failure one size down. hv-contentinai.css block 6h puts the four parts back for the
   ContentInAI panel in the 900–999 px band, where the column is this narrow but the panel is
   tall. */
@container (max-width: 380px) {
  .mkt-cd-stage { --u: clamp(8.6px, calc(3.571cqw - 1.393px), 13.2px); }
  .mkt-cd-desc { display: none; }
  .mkt-cd-colhead { display: none; }
  /* nth-child counts the (hidden) column head, so this keeps rows 2–4: three of the five
     proposals, under the one that is open. */
  .mkt-cd-list > .mkt-cd-row:nth-child(n+5) { display: none; }
}

/* --- sequence ---------------------------------------------------------------------- */

/* The list title is the first thing on the frame. */
.mkt-cd.is-armed .mkt-cd-head { animation: cd-rise 0.32s ease 0.1s backwards; }

/* The top proposal lands first — as a card whose values have not resolved yet; the pick that
   lights it up is the second animation on the same element, further down. */
.mkt-cd.is-armed .mkt-cd-card-title { animation: cd-rise 0.34s ease 0.35s backwards; }
.mkt-cd.is-armed .mkt-cd-metrics dt { animation: cd-fade 0.3s ease 0.45s backwards; }
.mkt-cd.is-armed .mkt-cd-query .mkt-cd-cap { animation: cd-fade 0.3s ease 0.52s backwards; }

/* The other four land in order, each with its reason and impact. */
.mkt-cd.is-armed .mkt-cd-colhead { animation: cd-fade 0.3s ease 0.42s backwards; }
.mkt-cd.is-armed .mkt-cd-row { animation: cd-row 0.36s cubic-bezier(0.22, 1, 0.36, 1) calc(0.5s + var(--i) * 0.15s) backwards; }

/* The pick: the card lights up and every loading slot resolves. */
.mkt-cd.is-armed .mkt-cd-card { animation: cd-card 0.4s cubic-bezier(0.22, 1, 0.36, 1) 0.28s backwards, cd-pick 0.5s ease 2s backwards; }
/* Each metric's skeleton hands over to the value that replaces it: same --k stagger, no gap. */
.mkt-cd.is-armed .mkt-cd-metrics dd::before { animation: cd-skel calc(1.87s + var(--k) * 0.1s) linear 0.28s backwards; }
.mkt-cd.is-armed .mkt-cd-desc::before { animation: cd-skel 2.02s linear 0.28s backwards; }
.mkt-cd.is-armed .mkt-cd-desc::after { animation: cd-skel 2.12s linear 0.28s backwards; }
.mkt-cd.is-armed .mkt-cd-query-val::before { animation: cd-skel 2.27s linear 0.28s backwards; }
.mkt-cd.is-armed .mkt-cd-metrics dd > * { animation: cd-value 0.36s cubic-bezier(0.2, 0.9, 0.3, 1.25) calc(2.15s + var(--k) * 0.1s) backwards; }
.mkt-cd.is-armed .mkt-cd-ring .is-value { animation: cd-ring 0.7s cubic-bezier(0.22, 1, 0.36, 1) 2.35s backwards; }
.mkt-cd.is-armed .mkt-cd-ring .mkt-cd-num { animation: cd-count 0.7s cubic-bezier(0.22, 1, 0.36, 1) 2.35s both; }
.mkt-cd.is-armed .mkt-cd-desc span { animation: cd-rise 0.36s ease 2.4s backwards; }
.mkt-cd.is-armed .mkt-cd-query b { animation: cd-rise 0.36s ease 2.55s backwards; }
/* Only once a topic is picked is there something to write: the hand-off to ContentInAI arrives
   the way the resolved metric values do. */
.mkt-cd.is-armed .mkt-cd-write { animation: cd-value 0.4s cubic-bezier(0.2, 0.9, 0.3, 1.25) 2.72s backwards; }


/* Waiting for marketing.js to mark the widget live: hold the first frame. */
.mkt-widget:not(.is-live) .mkt-cd.is-armed *,
.mkt-widget:not(.is-live) .mkt-cd.is-armed *::after,
.mkt-widget:not(.is-live) .mkt-cd.is-armed *::before { animation-play-state: paused !important; }

@media (prefers-reduced-motion: reduce) {
  .mkt-cd *,
  .mkt-cd *::after,
  .mkt-cd *::before { animation: none !important; }
}

@keyframes cd-fade { from { opacity: 0; } }
@keyframes cd-rise { from { opacity: 0; translate: 0 4px; } }
@keyframes cd-row { from { opacity: 0; translate: 0 6px; } }
@keyframes cd-value { from { opacity: 0; scale: 0.82; } }
@keyframes cd-card { from { opacity: 0; translate: 0 6px; } }
/* The pick: the resting card is already the lit one, so the early frames hold the quiet state. */
@keyframes cd-pick {
  from { border-color: #E3E5EC; background: #FFFFFF; box-shadow: none; }
  60% { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(91, 92, 230, 0.16); }
}
@keyframes cd-skel { 0%, 88% { opacity: 1; } 100% { opacity: 0; } }
@keyframes cd-ring { from { stroke-dashoffset: 100; } }
@keyframes cd-count { from { --cd-n: 0; } }

/* === features ============================================================== */
/*
 * Product page preview — the "Funkcje" section (partials/marketing/product-v2/features.blade.php
 * and widgets/v2/features-*.blade.php).
 *
 * Six feature cards, each with a snapshot of the feature as it looks in the app. The snapshots are
 * STILLS: no scroll animation, no armed sequence, nothing that waits for JavaScript and nothing
 * that answers the pointer — the section above ("Jak to działa") carries the motion, this one
 * carries the detail. A snapshot is a depiction, not a control, so no row, tile or mark inside it
 * has a hover state; only the card around it lifts, as the cards above this section do.
 *
 * The six snapshots are one family:
 *   frame   .mkt-fx-shot   — same box, same padding, same header line, same foot line
 *   header  .mkt-fx-top    — mono kicker on the left, the same period pill on the right, on all
 *                            six of them (product_v2_features.period, the window the "Jak to
 *                            działa" widgets filter on); where the kicker and the pill cannot
 *                            share the line the kicker takes its short form — the pill stays
 *   quiet   nothing that belongs to an operated table: no pager, no "sorted by" label, no rank
 *                            badges, no per-row counters, no axis dates — a card is read, not used
 *   scale   kicker 9.5 · micro 9 · row text 11.5 · value 15–17 · hero number 26
 *   colour  lilac = brand, green = good, amber = warning, red = problem, everything else grey
 * Class prefix mkt-fx-; every rule is scoped under .mkt-fx (the section), so nothing here can
 * reach the widgets of "Jak to działa" or the shared classes in marketing.css.
 *
 * Widths. The snapshot is the container (container-type: inline-size), so the layout follows the
 * card, not the viewport: ~683 / 473 px at 1440, ~580 / 398 at 1024, 364 / 244 at 768 and 242 for
 * every card in the 390 carousel. Three breakpoints handle it — 430px (two columns collapse),
 * 360px (the mention quotes its short form rather than losing its last line) and 330px (the phone
 * carousel: long labels swap for their short form).
 */

.mkt-fx {
  --fx-ink: #14162B;
  --fx-body: #4C5163;
  --fx-mut: #656B7E;
  --fx-soft: #7C8196;
  --fx-faint: #9AA0B1;
  --fx-line: #EDEEF2;
  --fx-edge: #DEE0E8;
  --fx-bg: #FAFAFC;
  --fx-brand: #5B5CE6;
  --fx-brand-soft: #C9CBF7;
  --fx-ok: #22C55E;
  --fx-ok-ink: #15803D;
  --fx-warn: #F59E0B;
  --fx-warn-ink: #B45309;
  --fx-bad: #EF4444;
  --fx-bad-ink: #B91C1C;
  --fx-neu: #94A3B8;
  --fx-mono: 'IBM Plex Mono', ui-monospace, monospace;
}

/* --- the shared frame --------------------------------------------------------- */

.mkt-fx .mkt-fx-shot {
  container-type: inline-size;
  height: 236px;
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 12px 13px;
  border: 1px solid var(--fx-line);
  border-radius: 12px;
  background: var(--fx-bg);
  overflow: hidden;
}

.mkt-fx .mkt-fx-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-height: 16px;
  flex: 0 0 auto;
}

.mkt-fx .mkt-fx-kicker {
  font-family: var(--fx-mono);
  font-size: 9.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fx-mut);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Two kickers are long enough to crowd the pill off the phone card; they carry a short form and
   the narrow breakpoint swaps them, exactly as the foot line does. Only one is ever on screen. */
.mkt-fx .mkt-fx-kicker-tight { display: none; }

.mkt-fx .mkt-fx-meta {
  flex: 0 0 auto;
  font-family: var(--fx-mono);
  font-size: 9px;
  letter-spacing: 0.04em;
  color: var(--fx-soft);
  background: #F1F2F6;
  border-radius: 999px;
  padding: 2.5px 7px;
  white-space: nowrap;
}

.mkt-fx .mkt-fx-body {
  flex: 1 1 auto;
  min-height: 0;
  /* The foot below is part of the frame: nothing in the body may ever reach it. */
  overflow: hidden;
}

.mkt-fx .mkt-fx-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex: 0 0 auto;
  font-family: var(--fx-mono);
  font-size: 9px;
  letter-spacing: 0.03em;
  color: var(--fx-soft);
  white-space: nowrap;
  min-width: 0;
}

.mkt-fx .mkt-fx-foot > span {
  overflow: hidden;
  text-overflow: ellipsis;
}

.mkt-fx .mkt-fx-foot b {
  font-weight: 600;
  color: var(--fx-ink);
  flex: 0 0 auto;
}

.mkt-fx .mkt-fx-foot b.is-ok { color: var(--fx-ok-ink); }
.mkt-fx .mkt-fx-foot b.is-warn { color: var(--fx-warn-ink); }

/* The long label and its short form: only one of the two is ever on screen. */
.mkt-fx .mkt-fx-foot-tight { display: none; }

.mkt-fx .mkt-fx-panel {
  border: 1px solid var(--fx-line);
  border-radius: 10px;
  background: #FFFFFF;
  min-width: 0;
}

.mkt-fx .mkt-fx-micro {
  font-family: var(--fx-mono);
  font-size: 9px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--fx-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mkt-fx .mkt-fx-delta {
  font-family: var(--fx-mono);
  font-size: 9.5px;
  font-weight: 500;
  white-space: nowrap;
}

.mkt-fx .mkt-fx-delta.is-up { color: var(--fx-ok-ink); }
.mkt-fx .mkt-fx-delta.is-down { color: var(--fx-bad-ink); }

.mkt-fx .mkt-fx-llm {
  display: block;
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
}

.mkt-fx .mkt-fx-llm .mkt-llm {
  display: block;
  width: 100%;
  height: 100%;
}

.mkt-fx .mkt-fx-llm.is-off {
  filter: grayscale(1);
  opacity: 0.26;
}

/* --- charts: one set of primitives for both line charts ----------------------- *
 * The plot is a box; the SVG stretches to it (preserveAspectRatio: none) and the stroke is kept
 * at its real width by vector-effect, so the curve fits any card width without distorting. Every
 * label, gridline and point marker is HTML positioned in per cent, which keeps the type crisp. */

/* No values on the gridlines and no dates under the curve (the period is the header pill), so the
   plot is the whole box — the room the labels used to take goes to the chart. */
.mkt-fx .mkt-fx-chart {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  padding: 3px 0 2px;
}

.mkt-fx .mkt-fx-plot {
  position: absolute;
  inset: 3px 0 2px;
}

.mkt-fx .mkt-fx-chart.is-wide .mkt-fx-plot { right: var(--fx-gutter, 86px); }

.mkt-fx .mkt-fx-plot svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.mkt-fx .mkt-fx-area { fill: rgba(91, 92, 230, 0.13); }

.mkt-fx .mkt-fx-line {
  fill: none;
  stroke: var(--fx-brand);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.mkt-fx .mkt-fx-line.is-rival {
  stroke: #A7AEC0;
  stroke-width: 1.6;
  stroke-dasharray: 4 3.5;
}

.mkt-fx .mkt-fx-rule {
  position: absolute;
  left: 0;
  right: 0;
  height: 0;
  border-top: 1px dashed #E5E7EF;
}

.mkt-fx .mkt-fx-dot {
  position: absolute;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--fx-brand);
  border: 1.5px solid #FFFFFF;
  translate: -50% -50%;
  box-shadow: 0 0 0 1px rgba(91, 92, 230, 0.45);
}

.mkt-fx .mkt-fx-dot.is-rival {
  background: #A7AEC0;
  box-shadow: 0 0 0 1px rgba(148, 163, 184, 0.5);
}

/* --- 1 · Brand Monitoring w top LLM ------------------------------------------- */

/* Explicit fractional rows: the nine tiles share the body's height exactly, whatever the card
   is tall, instead of growing past it. */
.mkt-fx .mkt-fx-mod-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  /* Dziewięć modeli: 3 × 3 (wcześniej sześć w 3 × 2). */
  grid-template-rows: repeat(3, minmax(0, 1fr));
  gap: 8px;
  height: 100%;
}

.mkt-fx .mkt-fx-mod-tile {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 7px;
  min-width: 0;
  padding: 9px 10px;
  border: 1px solid var(--fx-line);
  border-radius: 10px;
  background: #FFFFFF;
}

.mkt-fx .mkt-fx-mod-tile.is-lead {
  border-color: #D6DAFB;
  background: #F8F8FE;
}

.mkt-fx .mkt-fx-mod-head {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.mkt-fx .mkt-fx-mod-name {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: -0.012em;
  color: var(--fx-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mkt-fx .mkt-fx-mod-val {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.03em;
  line-height: 1;
  color: var(--fx-ink);
}

/* flex: 0 0 3px — a shorter tile must lose air, never the bar itself. */
.mkt-fx .mkt-fx-mod-bar {
  display: block;
  flex: 0 0 3px;
  height: 3px;
  border-radius: 3px;
  background: #EFF0F5;
  overflow: hidden;
}

.mkt-fx .mkt-fx-mod-bar i {
  display: block;
  height: 100%;
  width: var(--w);
  border-radius: inherit;
  background: #A3A4F2;
}

.mkt-fx .mkt-fx-mod-tile.is-lead .mkt-fx-mod-bar i { background: var(--fx-brand); }

/* Dziewięć modeli w 3 × 3: kafel jest wierszem, a nie słupkiem — znak i nazwa po lewej, liczba po
   prawej, pasek pod spodem na całą szerokość. W słupku (znak / liczba / pasek) trzy rzędy kafli nie
   mieściły się w wysokości kadru i dolny rząd tracił pasek pod krawędzią. */
.mkt-fx .mkt-fx-mod-grid .mkt-fx-mod-tile {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "head val" "bar bar";
  align-content: center;
  align-items: center;
  gap: 6px 6px;
  padding: 7px 9px;
}
.mkt-fx .mkt-fx-mod-grid .mkt-fx-mod-head { grid-area: head; }
.mkt-fx .mkt-fx-mod-grid .mkt-fx-mod-val { grid-area: val; font-size: 14.5px; }
.mkt-fx .mkt-fx-mod-grid .mkt-fx-mod-bar { grid-area: bar; }
/* Gdy kolumna kafla nie mieści pełnej nazwy (kadr węższy niż ~420px: 1000-1180px i telefony), nazwa
   ustępuje zamiast urywać się wielokropkiem: zostają znak modelu (nazwa w title kafla) i liczba.
   Dla czytnika ekranu nazwa zostaje w DOM. */
@container (max-width: 420px) {
  .mkt-fx .mkt-fx-mod-grid .mkt-fx-mod-name {
    position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
  }
}

/* --- 2 · Metryki i analiza GEO ------------------------------------------------- */

.mkt-fx .mkt-fx-met-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr);
  gap: 9px;
  height: 100%;
}

.mkt-fx .mkt-fx-met-score {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 9px 12px 7px;
  min-width: 0;
}

.mkt-fx .mkt-fx-met-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-width: 0;
}

.mkt-fx .mkt-fx-met-num {
  display: flex;
  align-items: baseline;
  gap: 5px;
}

/* line-height 1.25, not 1: at 1 the glyph box of Poppins 600 stands taller than the line box, so the
   hero number reports itself as overflowing its own line — harmless here, but a browser that rounds
   the other way would shave the digits. 1.25 puts the whole glyph inside the box. */
.mkt-fx .mkt-fx-met-num b {
  font-size: 26px;
  font-weight: 600;
  letter-spacing: -0.04em;
  line-height: 1.25;
  color: var(--fx-ink);
}

.mkt-fx .mkt-fx-met-num span {
  font-family: var(--fx-mono);
  font-size: 9px;
  color: var(--fx-faint);
}

.mkt-fx .mkt-fx-met-kpis {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

.mkt-fx .mkt-fx-kpi {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  padding: 7px 10px;
}

.mkt-fx .mkt-fx-kpi strong {
  display: flex;
  align-items: baseline;
  gap: 5px;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.025em;
  color: var(--fx-ink);
  white-space: nowrap;
}

/* --- 3 · Competitive Analysis --------------------------------------------------- */

.mkt-fx .mkt-fx-cmp-panel {
  display: flex;
  flex-direction: column;
  padding: 10px 12px 6px;
}

.mkt-fx .mkt-fx-cmp-chip {
  position: absolute;
  right: 0;
  width: calc(var(--fx-gutter, 86px) - 10px);
  translate: 0 -50%;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0 5px;
  font-size: 10px;
  line-height: 1.35;
  color: var(--fx-body);
  white-space: nowrap;
}

.mkt-fx .mkt-fx-cmp-chip i {
  width: 7px;
  height: 7px;
  border-radius: 2px;
  background: var(--fx-brand);
  flex: 0 0 auto;
}

.mkt-fx .mkt-fx-cmp-chip.is-rival i { background: #A7AEC0; }

.mkt-fx .mkt-fx-cmp-chip b {
  flex: 1 0 100%;
  padding-left: 12px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--fx-ink);
}

.mkt-fx .mkt-fx-cmp-chip.is-rival b { color: var(--fx-body); }

/* Shown only when the card is too narrow for the end labels. */
.mkt-fx .mkt-fx-cmp-legend {
  display: none;
  align-items: center;
  gap: 12px;
  margin-bottom: 4px;
  font-size: 10px;
  color: var(--fx-body);
}

.mkt-fx .mkt-fx-cmp-legend span {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

.mkt-fx .mkt-fx-cmp-legend i {
  width: 7px;
  height: 7px;
  border-radius: 2px;
  background: var(--fx-brand);
}

.mkt-fx .mkt-fx-cmp-legend .is-rival i { background: #A7AEC0; }
.mkt-fx .mkt-fx-cmp-legend b { font-weight: 600; color: var(--fx-ink); }

/* --- 4 · Sentiment Tracking ----------------------------------------------------- */

.mkt-fx .mkt-fx-sen-body {
  display: flex;
  flex-direction: column;
  gap: 11px;
}

.mkt-fx .mkt-fx-sen-split {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 0 0 auto;
}

.mkt-fx .mkt-fx-sen-bar {
  display: flex;
  gap: 2px;
  height: 7px;
}

.mkt-fx .mkt-fx-sen-bar i {
  display: block;
  width: var(--w);
  border-radius: 999px;
}

.mkt-fx .mkt-fx-sen-bar i.is-pos { background: var(--fx-ok); }
.mkt-fx .mkt-fx-sen-bar i.is-neu { background: var(--fx-neu); }
.mkt-fx .mkt-fx-sen-bar i.is-neg { background: var(--fx-bad); }

.mkt-fx .mkt-fx-sen-legend {
  display: flex;
  gap: 12px;
  min-width: 0;
}

.mkt-fx .mkt-fx-sen-legend > span {
  display: flex;
  align-items: baseline;
  gap: 4px;
  min-width: 0;
  font-size: 10px;
  color: var(--fx-mut);
}

.mkt-fx .mkt-fx-sen-legend i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  align-self: center;
  flex: 0 0 auto;
}

.mkt-fx .mkt-fx-sen-legend .is-pos i { background: var(--fx-ok); }
.mkt-fx .mkt-fx-sen-legend .is-neu i { background: var(--fx-neu); }
.mkt-fx .mkt-fx-sen-legend .is-neg i { background: var(--fx-bad); }

.mkt-fx .mkt-fx-sen-legend em {
  font-style: normal;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mkt-fx .mkt-fx-sen-legend b {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--fx-ink);
}

.mkt-fx .mkt-fx-sen-quote {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 7px;
  margin: 0;
  padding: 10px 11px 11px;
}

.mkt-fx .mkt-fx-sen-head {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.mkt-fx .mkt-fx-sen-model {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--fx-ink);
}

/* Three lines is what the panel holds at every card width. The mention has to be read whole
   inside them — a quote cut off mid-word reads as filler, not as evidence (the rule the Sentiment
   widget of "Jak to działa" states in its own source) — so below 360px the card quotes the short
   form of the same ChatGPT mention instead of clamping the long one. */
.mkt-fx .mkt-fx-sen-quote blockquote {
  margin: 0;
  font-size: 11.8px;
  line-height: 1.5;
  color: #262A47;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.mkt-fx .mkt-fx-sen-quote blockquote.is-tight { display: none; }

.mkt-fx .mkt-fx-badge {
  align-self: flex-start;
  margin-top: auto;
  font-size: 10px;
  font-weight: 600;
  border-radius: 999px;
  padding: 3px 9px;
}

.mkt-fx .mkt-fx-badge.is-pos {
  color: var(--fx-ok-ink);
  background: rgba(34, 197, 94, 0.12);
}

/* --- 5 · Lista priorytetów ------------------------------------------------------ */

.mkt-fx .mkt-fx-tsk-list {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 6px;
  height: 100%;
}

/* The budget this card lives on, in layout px (the page is zoomed to .9 above 1000px, so measure
   offsetHeight, never a client rect): body 165, against a lead row of ~62 and two rows of ~34 once
   the rank badges and the page counts are out of them. What the three rows do not spend is air
   between them (the list is space-between), which is where the card's calm comes from. */
.mkt-fx .mkt-fx-tsk-row {
  --c: #C4C8D6;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 7px 10px 7px 12px;
  border: 1px solid var(--fx-line);
  border-radius: 10px;
  background: #FFFFFF;
  box-shadow: inset 3px 0 0 var(--c);
}

.mkt-fx .mkt-fx-tsk-row.is-high { --c: var(--fx-bad); }
.mkt-fx .mkt-fx-tsk-row.is-med { --c: var(--fx-warn); }
.mkt-fx .mkt-fx-tsk-row.is-lead { background: #FCFCFE; }

.mkt-fx .mkt-fx-tsk-head {
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
}

.mkt-fx .mkt-fx-tag {
  flex: 0 0 auto;
  font-family: var(--fx-mono);
  font-size: 8.5px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 2.5px 6px;
  border-radius: 5px;
}

.mkt-fx .mkt-fx-tag.is-high { color: var(--fx-bad-ink); background: rgba(239, 68, 68, 0.1); }
.mkt-fx .mkt-fx-tag.is-med { color: var(--fx-warn-ink); background: rgba(245, 158, 11, 0.14); }
.mkt-fx .mkt-fx-tag.is-low { color: var(--fx-mut); background: #F4F5F8; }

/* This card exists to tell you what to do first, so a task never loses the end of its sentence to
   an ellipsis: the title wraps to a second line rather than being cut. Two lines is the ceiling —
   the row list has to stay inside the frame — and the list below is tuned so three rows plus the
   lead task's action always fit, whatever the card is wide. */
.mkt-fx .mkt-fx-tsk-title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 11.5px;
  line-height: 1.3;
  font-weight: 600;
  letter-spacing: -0.012em;
  color: var(--fx-ink);
  white-space: normal;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* The lead task's action sentence: it reads as the action, so nothing announces it.
   marketing.css closes its mobile block with `p { font-size: 14px !important }`, and below 641px
   that made this sentence LARGER than the title above it (14 against 11,5) and pushed the third
   row out of the frame. Only !important beats !important, and the class wins the tie on
   specificity — the same fix the task-list widget carries for its own sentence. */
.mkt-fx p.mkt-fx-tsk-body {
  margin: 0;
  font-size: 10.8px !important;
  line-height: 1.4;
  color: var(--fx-body);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* --- 6 · Zapytania AI ------------------------------------------------------------ */

/* --fx-models is the marks strip: nine 13px marks and eight 4px gaps. */
.mkt-fx .mkt-fx-qry-table {
  --fx-models: calc(9 * 13px + 8 * 4px);
  display: grid;
  grid-template-rows: auto repeat(4, minmax(0, 1fr));
  gap: 5px;
  height: 100%;
}

.mkt-fx .mkt-fx-qry-head,
.mkt-fx .mkt-fx-qry-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--fx-models) 34px 34px;
  align-items: center;
  gap: 9px;
}

.mkt-fx .mkt-fx-qry-head {
  padding: 0 9px 2px;
  font-family: var(--fx-mono);
  font-size: 8.5px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--fx-faint);
}

.mkt-fx .mkt-fx-qry-head > span {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The number headers are plain blocks, not the flex cells of a row: a flex box clips a long label
   on the left (the end of the text stays put) instead of ellipsing it on the right. Scoped to the
   two number cells so it cannot outrank the rule that drops the model column on a phone. */
.mkt-fx .mkt-fx-qry-head > .mkt-fx-qry-num {
  display: block;
  text-align: right;
}

.mkt-fx .mkt-fx-qry-row {
  padding: 2px 9px;
  min-height: 0;
  border: 1px solid var(--fx-line);
  border-radius: 8px;
  background: #FFFFFF;
}

.mkt-fx .mkt-fx-qry-text {
  font-size: 11.5px;
  color: var(--fx-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mkt-fx .mkt-fx-qry-models {
  display: flex;
  align-items: center;
  gap: 4px;
}

.mkt-fx .mkt-fx-qry-models .mkt-fx-llm {
  width: 13px;
  height: 13px;
}

.mkt-fx .mkt-fx-qry-num {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  font-family: var(--fx-mono);
  font-size: 10px;
  color: var(--fx-mut);
}

.mkt-fx .mkt-fx-qry-num b {
  font-weight: 600;
  color: var(--fx-ink);
}

.mkt-fx .mkt-fx-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 6px;
  font-family: 'Poppins', system-ui, sans-serif;
  font-size: 10.5px;
  font-weight: 600;
}

.mkt-fx .mkt-fx-pill.is-good { color: var(--fx-ok-ink); background: rgba(34, 197, 94, 0.13); }
.mkt-fx .mkt-fx-pill.is-ok { color: #0369A1; background: rgba(14, 165, 233, 0.13); }
.mkt-fx .mkt-fx-pill.is-warn { color: var(--fx-warn-ink); background: rgba(245, 158, 11, 0.16); }
.mkt-fx .mkt-fx-pill.is-bad { color: var(--fx-bad-ink); background: rgba(239, 68, 68, 0.12); }

/* --- hover: only the card, only under a real pointer ------------------------------ *
 * A snapshot is a depiction, not a control: nothing inside it reacts to the pointer, because
 * a row that lights up invites a click it cannot answer. The rows, tiles and model marks here
 * had a hover accent and the snapshot frame took a lilac tint from the card; both are gone,
 * together with the transitions that carried them. Nothing in here transitions, so there is
 * nothing for prefers-reduced-motion to switch off.
 *
 * The card around the picture still lifts — the same treatment the cards above this section
 * use — but it no longer borrows .hv-card-lg from marketing.css. That class is a bare :hover,
 * and a touch screen has no pointer that can leave the card: a tap latched the lift on and it
 * stayed on, 900 ms later and until the next tap somewhere else. The lift lives here now,
 * behind a pointer query, so a finger cannot turn it on at all. Same shadow as before, and no
 * border-color with it: the card carries `border: 1px solid #DEE0E8` inline, which an author
 * rule cannot outrank, so .hv-card-lg only ever painted the shadow on these six anyway.
 *
 * There is no link and no button inside these six snapshots; if one is ever added, its own
 * hover belongs on that control and nowhere else — and behind this same query. */

@media (hover: hover) and (pointer: fine) {
  .mkt-fx .mkt-fx-card:hover { box-shadow: 0 20px 44px -32px rgba(20, 22, 43, 0.45); }
}

/* --- the middle desktop card: 600px ------------------------------------------------ */

@container (max-width: 600px) {
  /* At 1024 the queries card is 580 wide and the longest prompt lands a few pixels past its cell;
     the model column gives them back: nine marks one pixel smaller, on 3px gaps (132px). */
  .mkt-fx .mkt-fx-qry-table { --fx-models: calc(9 * 12px + 8 * 3px); }
  .mkt-fx .mkt-fx-qry-models { gap: 3px; }
  .mkt-fx .mkt-fx-qry-models .mkt-fx-llm { width: 12px; height: 12px; }
}

/* --- narrow cards: 430px ---------------------------------------------------------- */

@container (max-width: 430px) {
  .mkt-fx .mkt-fx-met-grid {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr) auto;
    gap: 8px;
  }

  .mkt-fx .mkt-fx-met-kpis {
    flex-direction: row;
    gap: 6px;
  }

  .mkt-fx .mkt-fx-kpi {
    padding: 6px 8px;
    gap: 2px;
  }

  .mkt-fx .mkt-fx-kpi strong { font-size: 13px; gap: 4px; }
  .mkt-fx .mkt-fx-kpi .mkt-fx-delta { font-size: 8.5px; }
  .mkt-fx .mkt-fx-met-num b { font-size: 22px; }

  .mkt-fx .mkt-fx-chart.is-wide { --fx-gutter: 66px; }
  .mkt-fx .mkt-fx-cmp-chip { font-size: 9px; }
  .mkt-fx .mkt-fx-cmp-chip b { font-size: 10px; padding-left: 0; }

  /* A card this narrow cannot hold four one-line prompts, the marks and the mention count at once.
     The prompt is what the card is about, so it takes two lines; the table drops to three rows and
     the "Wzm." column steps out, leaving the marks to say which models found the brand. */
  .mkt-fx .mkt-fx-qry-table {
    --fx-models: calc(9 * 12px + 8 * 3px);
    grid-template-rows: auto repeat(3, minmax(0, 1fr));
  }

  .mkt-fx .mkt-fx-qry-head,
  .mkt-fx .mkt-fx-qry-row {
    grid-template-columns: minmax(0, 1fr) var(--fx-models) 34px;
    gap: 7px;
  }

  .mkt-fx .mkt-fx-qry-head > .mkt-fx-qry-men,
  .mkt-fx .mkt-fx-qry-row > .mkt-fx-qry-men { display: none; }

  .mkt-fx .mkt-fx-qry-table > .mkt-fx-qry-row:last-child { display: none; }

  .mkt-fx .mkt-fx-qry-text {
    font-size: 11px;
    line-height: 1.3;
    white-space: normal;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
  }

  .mkt-fx .mkt-fx-qry-models { gap: 3px; }
  .mkt-fx .mkt-fx-qry-models .mkt-fx-llm { width: 12px; height: 12px; }

  /* The tasks card is at its tightest here: every title needs two lines and the lead keeps both
     lines of its sentence. Measured on the page (offsetHeight, the body is zoomed to .9 above
     1000px): 74 + 40 + 40 and two 4px gaps = 162 of the 165 the body has, and it is the same 162
     on every card from 331 to 430 — so the rows give back the air they can afford on a wide card.
     The three pixels left are the whole margin: the sentence below is pinned to 10,8px with
     !important because marketing.css would otherwise make it 14 under a 641px viewport. */
  .mkt-fx .mkt-fx-tsk-list { gap: 4px; }
  .mkt-fx .mkt-fx-tsk-row { padding: 4px 9px 4px 11px; }
}

/* --- the narrow card: 360px --------------------------------------------------------- */

@container (max-width: 360px) {
  /* Below this the long mention needs a fourth line and the clamp cuts it mid-word (measured: it
     still reads whole in three lines on a 344px card, no longer on a 342px one), so the card
     quotes the short form of the same ChatGPT mention. The panel hands the three lines the pixels
     its padding was holding: on the narrowest card the legend wraps to two rows, and the quote box
     was being squeezed to two and a half lines — a line cut across the middle. */
  .mkt-fx .mkt-fx-sen-quote blockquote { display: none; }
  .mkt-fx .mkt-fx-sen-quote blockquote.is-tight { display: -webkit-box; }

  .mkt-fx .mkt-fx-sen-quote {
    gap: 6px;
    padding: 8px 11px 9px;
  }
}

/* --- the phone carousel: 330px ----------------------------------------------------- */

@container (max-width: 330px) {
  .mkt-fx .mkt-fx-foot-wide { display: none; }
  .mkt-fx .mkt-fx-foot-tight { display: inline; }

  /* The pill is what every header shares, so here the kicker is what gives way: the two long
     ones swap for their short form and all six headers keep the same two slots. */
  .mkt-fx .mkt-fx-kicker-wide { display: none; }
  .mkt-fx .mkt-fx-kicker-tight { display: block; }

  /* Przy dziewięciu modelach zostają trzy kolumny (2 kolumny dawałyby 5 wierszy z samotnym kaflem
     na dole). */
  .mkt-fx .mkt-fx-mod-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-template-rows: repeat(3, minmax(0, 1fr));
    gap: 6px;
  }


  .mkt-fx .mkt-fx-mod-tile { padding: 7px 9px; gap: 5px; }
  .mkt-fx .mkt-fx-mod-val { font-size: 15px; }

  .mkt-fx .mkt-fx-met-num b { font-size: 20px; }
  .mkt-fx .mkt-fx-kpi strong { font-size: 12.5px; }
  .mkt-fx .mkt-fx-kpi .mkt-fx-micro { font-size: 8px; letter-spacing: 0.04em; }
  /* Three values, three short labels: the changes would only truncate at this width. */
  .mkt-fx .mkt-fx-kpi .mkt-fx-delta { display: none; }

  /* No room for end labels next to the lines: they become a legend above the chart. */
  .mkt-fx .mkt-fx-chart.is-wide { --fx-gutter: 0px; padding-right: 0; }
  .mkt-fx .mkt-fx-cmp-chip { display: none; }
  .mkt-fx .mkt-fx-cmp-legend { display: flex; }

  /* Three percentages told apart by the colour of a dot would be the one place in the family where
     meaning rests on colour alone, so the words stay and the value moves under them; the quote
     gives back the line the legend takes. */
  .mkt-fx .mkt-fx-sen-legend { gap: 7px; }

  .mkt-fx .mkt-fx-sen-legend > span {
    flex-wrap: wrap;
    align-items: center;
    gap: 0 4px;
  }

  .mkt-fx .mkt-fx-sen-legend em { font-size: 8.5px; }

  .mkt-fx .mkt-fx-sen-legend b {
    flex: 1 0 100%;
    padding-left: 10px;
    font-size: 10.5px;
  }

  /* Phone width: three whole task titles read better than three truncated ones with their tags,
     so the impact moves entirely to the coloured edge and the lead task's action sentence steps out
     (the title wraps to two lines here as it does everywhere — the base rule handles that). */
  .mkt-fx .mkt-fx-tag,
  .mkt-fx p.mkt-fx-tsk-body { display: none; }

  .mkt-fx .mkt-fx-tsk-row {
    padding-left: 12px;
    box-shadow: inset 4px 0 0 var(--c);
  }

  /* 169px of text cell against titles that run to 331px: at the section's 11,5px every one of the
     three would need a third line and lose its ending. A phone-sized 10px brings them all inside
     two lines, and the third line stays available so a longer task could never be cut either — the
     three rows still fit the frame at their tallest. */
  .mkt-fx .mkt-fx-tsk-title {
    font-size: 10px;
    line-height: 1.3;
    -webkit-line-clamp: 3;
  }

  /* Only the prompt and its position survive here; two lines of text beat four columns of nothing.
     The position cell is trimmed to the pill it holds and the type steps down to 10px, which is
     what keeps the longest prompt (309px of text) inside two lines of a 167px cell. */
  /* 30px is the width of the "Poz." header, not of the pill it sits over — a clipped column header
     would be the one truncation the reader cannot forgive. The row padding gives the text back the
     six pixels that costs. */
  .mkt-fx .mkt-fx-qry-head,
  .mkt-fx .mkt-fx-qry-row { grid-template-columns: minmax(0, 1fr) 30px; gap: 5px; }
  .mkt-fx .mkt-fx-qry-head { padding-inline: 6px; }
  .mkt-fx .mkt-fx-qry-row { padding-inline: 6px; }
  .mkt-fx .mkt-fx-qry-models { display: none; }
  .mkt-fx .mkt-fx-qry-text { font-size: 9.5px; }
}

/* === leaderboard =========================================================== */
/*
 * Product page preview — Monitoring → "Porównanie z konkurencją" (widgets/v2/leaderboard.blade.php).
 *
 * The frame answers three questions and carries nothing else: who leads, by how much, and that the
 * order moved. So every rule below describes one board — rank · brand · share-of-voice bar · share ·
 * the visibility score the ranking is sorted by — over a status line that ends on the lead instead
 * of a "gotowe" alert. What the previous pass had here and this one does not: the Średnia / Wg LLM
 * toggle, the sort caption, the Śr. pozycja column (the sibling "Pozycja" preview is that metric),
 * the per-row change chips (the ▲▼ next to the rank already says who moved), the folded-brands
 * footnote and the three summary tiles. The space they used up goes to the five rows.
 *
 * The sequence that leads there is a set of keyframes whose early frames hold the earlier states.
 * They apply only once the widget is armed by its inline script, sit paused until marketing.js marks
 * the widget live, and restart by themselves each time the accordion shows the widget again
 * (display: none cancels CSS animations).
 *
 * Timeline (s): 0–0.25 the filter chips and the column head · 0.15–0.75 the rows arrive in the
 *               PREVIOUS period's order, every pill showing the rank it had then · 0.6–1.7 the bars
 *               grow, the shares count up, the score rings land · 1.9–2.5 the board sorts itself,
 *               the climbing rows flying over the ones they pass · 2.45–2.9 the pills flip and the
 *               ▲▼ chips pop · 2.58–3.11 your row lights up and the status line crosses over to the lead.
 *
 * Class prefix mkt-lb2-.
 */

@property --lb-n { syntax: '<integer>'; inherits: false; initial-value: 0; }
@property --lb-d { syntax: '<integer>'; inherits: false; initial-value: 0; }

.mkt-lb2 {
  container-type: inline-size;
  gap: 0;
  --ink: #14162B;
  --muted: #656B7E;
  --line: #EDEEF2;
  --sep: ',';
  --rankw: 46px;
  --brandw: 128px;
  --valw: 58px;
  --scorew: 32px;
  --colgap: 8px;
  --rowgap: 6px;
  --ringw: 28px;
}
.mkt-lb2.is-dot { --sep: '.'; }

.mkt-lb2-stage { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; gap: 10px; }

.mkt-lb2-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Counters: the integer sits in --to (and the tenth in --tod); the real number stays in the
   visually hidden span, so a blocked stylesheet still reads correctly. */
.mkt-lb2-num { --lb-n: var(--to); --lb-d: var(--tod, 0); counter-reset: lbn var(--lb-n) lbd var(--lb-d); }
.mkt-lb2-num::after { content: counter(lbn); }
.mkt-lb2-num.is-dec::after { content: counter(lbn) var(--sep) counter(lbd); }

/* --- status line + filter chips ----------------------------------------------
 * Four lines share one grid cell, so the row is as tall as the tallest of them from the first frame
 * on and the board below never shifts as the line changes. The last of them is the settled state:
 * the run's narration fades through, the lead sentence stays. */

.mkt-lb2-top { display: flex; align-items: flex-start; gap: 8px; flex: 0 0 auto; min-height: 18px; }
/* Aligned to the first line of the text rather than centred, so a lead sentence that wraps on a
   narrow preview keeps the mark next to its first line. */
.mkt-lb2-state { position: relative; width: 14px; height: 14px; flex: 0 0 auto; margin-top: 1.5px; }
.mkt-lb2-spinner {
  position: absolute;
  inset: 0;
  box-sizing: border-box;
  border-radius: 50%;
  border: 2px solid #E3E4FA;
  border-top-color: #5B5CE6;
  opacity: 0;
}
.mkt-lb2-tick { position: absolute; inset: 0; border-radius: 50%; background: #22C55E; }
.mkt-lb2-tick::after {
  content: "";
  position: absolute;
  left: 4.7px;
  top: 3px;
  width: 3.2px;
  height: 6.1px;
  border: solid #FFFFFF;
  border-width: 0 1.6px 1.6px 0;
  transform: rotate(45deg);
}
.mkt-lb2-steps {
  display: grid;
  align-items: start;
  flex: 1 1 auto;
  min-width: 0;
  font-size: 12.5px;
  font-weight: 500;
  line-height: 1.35;
  color: #262A47;
}
.mkt-lb2-steps > span {
  grid-area: 1 / 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  opacity: 0;
}
/* The lead the run ends on is the settled state — the one line that wraps instead of being clipped,
   because it is what a viewer without JS or with reduced motion reads. */
.mkt-lb2-steps > .is-result {
  opacity: 1;
  white-space: normal;
  overflow: visible;
  text-wrap: pretty;
}
.mkt-lb2-steps .is-short { display: none; }

.mkt-lb2-filters { display: flex; gap: 6px; flex: 0 0 auto; }
.mkt-lb2-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border: 1px solid #E3E5EC;
  border-radius: 999px;
  background: #FFFFFF;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px;
  color: #4C5163;
  white-space: nowrap;
}
.mkt-lb2-chip::after {
  content: "";
  width: 4px;
  height: 4px;
  border: solid #9AA0B4;
  border-width: 0 1.2px 1.2px 0;
  transform: translateY(-1px) rotate(45deg);
}

/* --- the board ----------------------------------------------------------------
 * One grid per row: rank · brand · the share-of-voice bar · the share · the score. The column head
 * shares the template, so every label sits over its own column. The five rows are equal fractions of
 * the board, which is what lets the sort translate them by whole row heights — and, with the three
 * bands that used to sit underneath gone, what gives each row the air the owner asked for. */

.mkt-lb2-board {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-rows: auto repeat(5, minmax(0, 1fr));
  row-gap: var(--rowgap);
  margin-inline: -6px;
}

.mkt-lb2-head,
.mkt-lb2-row {
  display: grid;
  grid-template-columns:
    var(--rankw) minmax(0, var(--brandw)) minmax(0, 1fr)
    var(--valw) var(--scorew);
  align-items: center;
  column-gap: var(--colgap);
  padding-inline: 6px;
}

.mkt-lb2-head {
  padding-bottom: 6px;
  border-bottom: 1px solid var(--line);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #9AA0B4;
  white-space: nowrap;
}
.mkt-lb2-head > span { overflow: hidden; text-overflow: ellipsis; }
.mkt-lb2-head .is-sov { grid-column: 3 / span 2; }
/* The score column is only as wide as the ring, so its label is wider than its own track on the
   narrow previews. It may spill into the gap next to it — the cell to its left is the far-left
   "share of voice" label, so there is nothing there to collide with — but it must never ellipsise
   into "WYN…". */
.mkt-lb2-head .is-score { text-align: center; overflow: visible; }

.mkt-lb2-row { position: relative; background: #FFFFFF; border-radius: 10px; }
/* Positioned cells paint after the positioned ::before below, so the wash never covers them. */
.mkt-lb2-row > * { position: relative; z-index: 1; }
/* The climbing row flies over the one it passes. */
.mkt-lb2-row.is-up { z-index: 3; }
.mkt-lb2-row.is-down { z-index: 2; }

/* Your brand's row: the app highlights the main brand in the leaderboard. */
.mkt-lb2-row.is-you::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  background: #EEF0FE;
  box-shadow: inset 0 0 0 1px #D6DAFB;
  pointer-events: none;
}

.mkt-lb2-rank { display: flex; align-items: center; gap: 5px; min-width: 0; }
/* The app's "#" column uses the neutral position pill. */
.mkt-lb2-pill {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  flex: 0 0 auto;
  border-radius: 7px;
  background: #F1F2F6;
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
  color: #262A47;
}
.mkt-lb2-row.is-you .mkt-lb2-pill { background: #5B5CE6; color: #FFFFFF; }
/* The flip: the settled frame is the new rank, with the rank the brand had stacked under it —
   the sequence below holds that older number visible until the sort lands. */
.mkt-lb2-flip { display: grid; }
.mkt-lb2-flip i { grid-area: 1 / 1; font-style: normal; }
.mkt-lb2-flip .is-prev { opacity: 0; }

/* The arrow and its number are one token: never let the chip break across two lines. Only the rows
   that actually changed place carry one. */
.mkt-lb2-move { font-family: 'IBM Plex Mono', monospace; font-size: 10px; font-style: normal; line-height: 1; white-space: nowrap; }
.mkt-lb2-move.is-up { color: #15803D; }
.mkt-lb2-move.is-down { color: #B91C1C; }

.mkt-lb2-brand { display: flex; align-items: center; gap: 7px; min-width: 0; }
.mkt-lb2-mark {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
  border-radius: 6px;
  background: #E8F1FB;
  color: #2563EB;
}
.mkt-lb2-mark svg { display: block; width: 13px; height: 13px; }
.mkt-lb2-row.is-you .mkt-lb2-mark { background: #5B5CE6; color: #FFFFFF; }
.mkt-lb2-name {
  min-width: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mkt-lb2-row.is-you .mkt-lb2-name { color: #4B4CD8; }
.mkt-lb2-name .is-short { display: none; }
.mkt-lb2-you {
  flex: 0 0 auto;
  padding: 1px 5px;
  border-radius: 5px;
  background: #5B5CE6;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #FFFFFF;
}

.mkt-lb2-bar { display: block; height: 11px; border-radius: 999px; background: #F1F2F6; overflow: hidden; }
.mkt-lb2-bar i {
  display: block;
  height: 100%;
  width: calc(var(--w) * 1%);
  border-radius: inherit;
  background: #AEB3C7;
  transform-origin: 0 50%;
}
.mkt-lb2-row.is-you .mkt-lb2-bar { background: #DFE1FA; }
.mkt-lb2-row.is-you .mkt-lb2-bar i { background: linear-gradient(90deg, #7A7BF0, #5B5CE6); }

.mkt-lb2-val {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 3px;
  min-width: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.mkt-lb2-unit { font-size: 11px; font-weight: 500; color: #9AA0B4; margin-left: -2px; }

/* The score the ranking is sorted by, as the app's score circle: the band lives in the ring, the
   number stays ink, so a row carries one colour signal, not three. */
.mkt-lb2-score { display: flex; align-items: center; justify-content: center; }
.mkt-lb2-ring { position: relative; width: var(--ringw); height: var(--ringw); flex: 0 0 auto; }
.mkt-lb2-ring svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
.mkt-lb2-ring circle { fill: none; stroke-width: 4.5; }
.mkt-lb2-ring .is-track { stroke: #EDEEF2; }
.mkt-lb2-ring .is-value {
  stroke: #22C55E;
  stroke-linecap: round;
  stroke-dasharray: 100;
  stroke-dashoffset: calc(100 - var(--score));
}
/* The bands of the app's score circle: ≤40 error, ≤70 warning, else success. */
.mkt-lb2-score.is-warn .mkt-lb2-ring .is-value { stroke: #F59E0B; }
.mkt-lb2-score.is-bad .mkt-lb2-ring .is-value { stroke: #EF4444; }
.mkt-lb2-ring b {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* Wide previews (the home panel and the single-column 768 layout): everything one notch larger. */
@container (min-width: 620px) {
  .mkt-lb2-stage {
    gap: 12px;
    --rankw: 56px;
    --brandw: 172px;
    --valw: 70px;
    --scorew: 40px;
    --colgap: 12px;
    --rowgap: 8px;
    --ringw: 34px;
  }
  .mkt-lb2-steps { font-size: 13.5px; }
  .mkt-lb2-chip { font-size: 10px; padding: 3px 9px; }
  .mkt-lb2-head { font-size: 9.5px; padding-bottom: 7px; }
  .mkt-lb2-row { border-radius: 12px; }
  .mkt-lb2-pill { width: 28px; height: 28px; font-size: 15px; border-radius: 8px; }
  .mkt-lb2-move { font-size: 11px; }
  .mkt-lb2-mark { width: 24px; height: 24px; border-radius: 7px; }
  .mkt-lb2-mark svg { width: 15px; height: 15px; }
  .mkt-lb2-name { font-size: 15px; }
  .mkt-lb2-you { font-size: 9px; padding: 2px 6px; }
  .mkt-lb2-bar { height: 13px; }
  .mkt-lb2-val { font-size: 16px; }
  .mkt-lb2-unit { font-size: 12px; }
  .mkt-lb2-ring b { font-size: 13.5px; }
  /* The five tracks divide whatever height the panel has, so on the tall single-column layout they
     grow past 70px. The row's own content is ~30px: past ~56px the wash stops reading as "this row
     is yours" and starts reading as a block sitting on the board. Cap it and let the rest of the
     track stay air between the rows — the track pitch is untouched, so the sort still travels by
     whole rows. */
  .mkt-lb2-row.is-you::before { inset: max(0px, calc((100% - 56px) / 2)) 0; }
}

/* Narrower previews (the two-column desktop layout below ~1100px). */
@container (max-width: 519px) {
  .mkt-lb2-stage {
    gap: 8px;
    --rankw: 42px;
    --brandw: 96px;
    --valw: 50px;
    --scorew: 26px;
    --colgap: 7px;
    --rowgap: 5px;
    --ringw: 23px;
  }
  .mkt-lb2-name .is-full { display: none; }
  .mkt-lb2-name .is-short { display: inline; }
  /* The lead switches to its short wording at the same width the names do: below this the full
     sentence no longer fits the column beside the filter chips, and a two-line status line pushes
     the board down and reads as a paragraph rather than a result. */
  .mkt-lb2-steps .is-full { display: none; }
  .mkt-lb2-steps .is-short { display: inline; }
  .mkt-lb2-steps { font-size: 11.5px; }
  .mkt-lb2-head { font-size: 8.5px; padding-bottom: 5px; }
  .mkt-lb2-row { border-radius: 9px; }
  .mkt-lb2-pill { width: 20px; height: 20px; font-size: 11.5px; border-radius: 6px; }
  .mkt-lb2-move { font-size: 9px; }
  .mkt-lb2-brand { gap: 6px; }
  .mkt-lb2-mark { width: 17px; height: 17px; border-radius: 5px; }
  .mkt-lb2-mark svg { width: 11px; height: 11px; }
  .mkt-lb2-name { font-size: 11.5px; }
  .mkt-lb2-you { font-size: 8px; padding: 1px 4px; }
  .mkt-lb2-bar { height: 9px; }
  .mkt-lb2-val { font-size: 12px; }
  .mkt-lb2-unit { font-size: 10px; }
  .mkt-lb2-ring b { font-size: 10px; }
}

/* The phone column: one chip instead of two and the short lead, so the status line stays one line
   and the five rows keep their air. The chip that goes is the LLM one and the period stays — the
   same half the sibling query-list keeps at this width, for the same reason: the period is what the
   28,4 % and the 72 were counted over, and nothing else on the board says it, while "all LLMs" is
   the default scope of a Monitoring view. It is also the narrower of the two, which is what buys
   the lead sentence its single line. */
@container (max-width: 405px) {
  .mkt-lb2-stage {
    gap: 7px;
    --rankw: 38px;
    --brandw: 80px;
    --valw: 46px;
    --scorew: 28px;
    --colgap: 6px;
  }
  .mkt-lb2-filters .mkt-lb2-chip:first-child { display: none; }
  .mkt-lb2-steps { font-size: 11px; }
  .mkt-lb2-head { font-size: 8px; letter-spacing: 0.04em; }
  .mkt-lb2-pill { width: 18px; height: 18px; font-size: 10.5px; border-radius: 5px; }
  .mkt-lb2-move { font-size: 8.5px; }
  .mkt-lb2-mark { width: 16px; height: 16px; }
  .mkt-lb2-mark svg { width: 10px; height: 10px; }
  .mkt-lb2-name { font-size: 10.5px; }
  .mkt-lb2-bar { height: 8px; }
  .mkt-lb2-val { font-size: 11.5px; }
  .mkt-lb2-unit { font-size: 9.5px; }
  .mkt-lb2-ring b { font-size: 9px; }
}

/* --- sequence ------------------------------------------------------------------ */

.mkt-lb2.is-armed .mkt-lb2-chip { animation: lb-rise 0.28s ease calc(var(--i) * 0.07s) backwards; }
.mkt-lb2.is-armed .mkt-lb2-head { animation: lb-fade 0.3s ease 0.08s backwards; }

/* The rows arrive in the previous period's order (lb-sort's backwards fill holds them there) and
   only travel to their new place once the ranking is computed. lb-pop must not touch `translate`:
   the later animation in the list wins while both are in effect. */
.mkt-lb2.is-armed .mkt-lb2-row {
  animation:
    lb-pop 0.32s ease calc(0.15s + var(--prev) * 0.08s) backwards,
    lb-sort 0.6s cubic-bezier(0.5, 0, 0.2, 1) calc(1.9s + var(--sd)) backwards;
}
.mkt-lb2.is-armed .mkt-lb2-row.is-up {
  animation:
    lb-pop 0.32s ease calc(0.15s + var(--prev) * 0.08s) backwards,
    lb-sort-up 0.6s cubic-bezier(0.5, 0, 0.2, 1) calc(1.9s + var(--sd)) backwards;
}
.mkt-lb2.is-armed .mkt-lb2-row.is-you::before { animation: lb-fade 0.45s ease 2.58s backwards; }

.mkt-lb2.is-armed .mkt-lb2-bar i { animation: lb-bar 0.7s cubic-bezier(0.22, 1, 0.36, 1) calc(0.6s + var(--prev) * 0.07s) backwards; }
.mkt-lb2.is-armed .mkt-lb2-val .mkt-lb2-num { animation: lb-count 0.75s ease-out calc(0.6s + var(--prev) * 0.07s) both; }
.mkt-lb2.is-armed .mkt-lb2-score { animation: lb-pop 0.32s cubic-bezier(0.2, 0.9, 0.3, 1.3) calc(0.95s + var(--prev) * 0.07s) backwards; }
.mkt-lb2.is-armed .mkt-lb2-ring .is-value { animation: lb-ring 0.6s cubic-bezier(0.22, 1, 0.36, 1) calc(0.95s + var(--prev) * 0.07s) backwards; }

/* The sort's result: the pill flips to the new rank and the ▲▼ chip appears. */
.mkt-lb2.is-armed .mkt-lb2-flip .is-prev { animation: lb-flip-out 0.28s ease calc(2.45s + var(--sd)) backwards; }
.mkt-lb2.is-armed .mkt-lb2-flip .is-now { animation: lb-flip-in 0.28s ease calc(2.45s + var(--sd)) backwards; }
.mkt-lb2.is-armed .mkt-lb2-move { animation: lb-pop 0.3s cubic-bezier(0.2, 0.9, 0.3, 1.35) calc(2.58s + var(--now) * 0.05s) backwards; }

/* The status line: three narrated steps that fade through, then the lead — which stays, because it
   is the settled frame. The lead starts rising 0.24s before the sort line has finished fading, so
   the two cross over instead of handing off through an empty cell: with no overlap the row went
   blank for ~120ms on an already-settled frame and read as a dropped frame. The 0.24s is the
   benchmark audit-crawl's own overlap between its last two lines. */
.mkt-lb2.is-armed .mkt-lb2-spinner { animation: lb-spin 0.7s linear infinite, lb-hold 3.05s linear both; }
.mkt-lb2.is-armed .mkt-lb2-tick { animation: lb-pop 0.34s cubic-bezier(0.2, 0.9, 0.3, 1.3) 3.05s both; }
.mkt-lb2.is-armed .mkt-lb2-steps > .is-collect { animation: lb-window 0.85s ease 0s both; }
.mkt-lb2.is-armed .mkt-lb2-steps > .is-count { animation: lb-window 1s ease 0.85s both; }
.mkt-lb2.is-armed .mkt-lb2-steps > .is-sort { animation: lb-window 1.2s ease 1.85s both; }
.mkt-lb2.is-armed .mkt-lb2-steps > .is-result { animation: lb-rise 0.3s ease 2.81s both; }

/* Waiting for marketing.js to mark the widget live: hold the first frame. */
.mkt-widget:not(.is-live) .mkt-lb2.is-armed *,
.mkt-widget:not(.is-live) .mkt-lb2.is-armed *::after,
.mkt-widget:not(.is-live) .mkt-lb2.is-armed *::before { animation-play-state: paused !important; }

@media (prefers-reduced-motion: reduce) {
  .mkt-lb2 *,
  .mkt-lb2 *::after,
  .mkt-lb2 *::before { animation: none !important; }
}

@keyframes lb-fade { from { opacity: 0; } }
@keyframes lb-rise { from { opacity: 0; translate: 0 4px; } }
@keyframes lb-pop { from { opacity: 0; scale: 0.95; } }
@keyframes lb-window { 0% { opacity: 0; translate: 0 3px; } 14%, 86% { opacity: 1; translate: 0 0; } 100% { opacity: 0; translate: 0 -3px; } }
@keyframes lb-sort { from { translate: 0 calc(var(--dy) * (100% + var(--rowgap))); } }
@keyframes lb-sort-up {
  0% { translate: 0 calc(var(--dy) * (100% + var(--rowgap))); box-shadow: none; }
  35%, 65% { box-shadow: 0 10px 22px -12px rgba(20, 22, 43, 0.55); }
  100% { translate: 0 0; box-shadow: none; }
}
@keyframes lb-bar { from { transform: scaleX(0); } }
@keyframes lb-ring { from { stroke-dashoffset: 100; } }
@keyframes lb-count { from { --lb-n: 0; --lb-d: 0; } }
@keyframes lb-flip-out { from { opacity: 1; translate: 0 0; } to { opacity: 0; translate: 0 -80%; } }
@keyframes lb-flip-in { from { opacity: 0; translate: 0 80%; } to { opacity: 1; translate: 0 0; } }
@keyframes lb-spin { to { rotate: 360deg; } }
@keyframes lb-hold { 0%, 96% { opacity: 1; } 100% { opacity: 0; } }

/* === position ============================================================== */
/*
 * Product page preview — Monitoring → "Pozycja" (widgets/v2/position.blade.php).
 *
 * Every rule below describes the FINISHED view: nine models, each with the ranking it gives the
 * brand's category (the competitors around Nike) and Nike's position pill, under one status line
 * that ends on how the period went. The sequence that leads there is a set of keyframes whose
 * early frames hold the earlier states. They apply only once the widget is armed by its inline
 * script, sit paused until marketing.js marks the widget live, and restart by themselves each time
 * the accordion shows the widget again (display: none cancels CSS animations).
 *
 * The frame is the ladder plus the pair of filter chips the whole Monitoring group carries in the
 * same top-right slot, and nothing else — no hint line, no change column, no counter strip, and no
 * header word over the model names — so the rows take the height that furniture used to spend, and
 * the status line stays on the one line its siblings' all take.
 *
 * Timeline (s): 0–0.25 the scale and the chips · 0.2–1.35 the nine rankings settle, row by row, pill
 *               by pill · 1.2–2.35 the previous period is compared · 2.2–3.3 a fresh ChatGPT answer
 *               moves Nike past Adidas (the two chips step into their own lanes, pass and land; the
 *               pill flips 2 → 1) · 3.15–3.5 the tick and the result line — which is also the
 *               settled (no-JS, reduced-motion) state, so the frame never rests on a "monitoring
 *               updated" alert. Every step overlaps the one before it, so the status row never
 *               blinks through an empty frame.
 *
 * Class prefix mkt-pos2-. The only shared class reused is .mkt-llm (model logo).
 */

.mkt-pos2 {
  container-type: inline-size;
  gap: 10px;
  --ink: #14162B;
  /* The name column is as wide as the longest model name, "AI Overviews", at each bucket's size
     (logo + gap + text, measured, plus 2px so no rounding ellipsises it): 102px here, 94px and
     78px in the narrow buckets below. */
  --namew: 102px;
  --pillw: 26px;
  --gap: 6px;
  /* The lane the two chips that swap rungs travel in while they pass each other: one rides above
     the row's baseline, the other below it, and 2 x --lift clears the chip's own height, so neither
     ever covers the other's label. Re-set wherever the chip's height changes. */
  --lift: 12.5px;
}

.mkt-pos2-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- status line ------------------------------------------------------------- */

.mkt-pos2-top { display: flex; align-items: flex-start; gap: 8px; flex: 0 0 auto; min-height: 18px; }
/* Aligned to the first line of the text rather than centred, so a result line that wraps on a
   narrow preview keeps the mark next to its first line. */
.mkt-pos2-state { position: relative; width: 14px; height: 14px; flex: 0 0 auto; margin-top: 1.5px; }
.mkt-pos2-spinner {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid #E3E4FA;
  border-top-color: #5B5CE6;
  opacity: 0;
}
.mkt-pos2-tick { position: absolute; inset: 0; border-radius: 50%; background: #22C55E; }
.mkt-pos2-tick::after {
  content: "";
  position: absolute;
  left: 4.7px;
  top: 3px;
  width: 3.2px;
  height: 6.1px;
  border: solid #FFFFFF;
  border-width: 0 1.6px 1.6px 0;
  transform: rotate(45deg);
}
/* The four lines share one grid cell, so the row is as tall as the tallest of them from the first
   frame on and the ladder below never shifts as the line changes. */
.mkt-pos2-steps {
  display: grid;
  align-items: start;
  flex: 1 1 auto;
  min-width: 0;
  font-size: 12.5px;
  font-weight: 500;
  line-height: 1.35;
  color: #262A47;
}
.mkt-pos2-steps > span {
  grid-area: 1 / 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  opacity: 0;
}
/* The result the run ends on is the settled state — the one line that wraps instead of being
   clipped, because it is what a viewer without JS or with reduced motion reads. */
.mkt-pos2-steps > .is-result {
  opacity: 1;
  white-space: normal;
  overflow: visible;
  text-wrap: pretty;
}

/* The scope the ladder was read over, in the slot and the shape every sibling preview uses. */
.mkt-pos2-filters { display: flex; gap: 6px; flex: 0 0 auto; margin-left: auto; }
.mkt-pos2-fchip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border: 1px solid #E3E5EC;
  border-radius: 999px;
  background: #FFFFFF;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px;
  color: #4C5163;
  white-space: nowrap;
}
.mkt-pos2-fchip::after {
  content: "";
  width: 4px;
  height: 4px;
  border: solid #9AA0B4;
  border-width: 0 1.2px 1.2px 0;
  transform: translateY(-1px) rotate(45deg);
}

/* --- the ladder ---------------------------------------------------------------
 * One grid per row: model · the four rungs of the ranking · the position pill · the change.
 * The scale header shares the template, so the rung numbers sit exactly over the chips. */

.mkt-pos2-grid {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-rows: auto repeat(9, minmax(0, 1fr));
  row-gap: 4px;
}

.mkt-pos2-row,
.mkt-pos2-scale {
  display: grid;
  grid-template-columns: var(--namew) minmax(0, 1fr) var(--pillw);
  align-items: center;
  column-gap: 8px;
}
.mkt-pos2-row { position: relative; }
.mkt-pos2-row + .mkt-pos2-row { border-top: 1px solid #F4F5F8; }
/* Positioned cells paint after the positioned ::before below, so the wash never covers them. */
.mkt-pos2-row > * { position: relative; }

/* The wash that marks the row the fresh answer lands in; behind the chips, never over them. */
.mkt-pos2-row.is-move::before {
  content: "";
  position: absolute;
  inset: -1px -7px;
  z-index: 0;
  border-radius: 9px;
  background: rgba(91, 92, 230, 0.10);
  opacity: 0;
  pointer-events: none;
}

.mkt-pos2-scale b {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9px;
  font-weight: 500;
  color: #9AA0B4;
  text-align: center;
}

.mkt-pos2-model { display: flex; align-items: center; gap: 7px; min-width: 0; }
.mkt-pos2 .mkt-pos2-model .mkt-llm { width: 15px; height: 15px; }
.mkt-pos2-name {
  min-width: 0;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mkt-pos2-track {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: var(--gap);
  align-items: center;
  min-width: 0;
}
.mkt-pos2-chip {
  display: block;
  min-width: 0;
  padding: 4px 6px;
  border: 1px solid transparent;
  border-radius: 7px;
  background: #F4F5F9;
  font-size: 10.5px;
  line-height: 1.25;
  color: #5F6478;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mkt-pos2-chip.is-you {
  border-color: #C2C4F4;
  background: #EEF0FE;
  color: #3F40C9;
  font-weight: 600;
  box-shadow: 0 8px 16px -12px rgba(91, 92, 230, 0.9);
}
.mkt-pos2-chip .is-short { display: none; }

.mkt-pos2-pill {
  display: grid;
  place-items: center;
  width: var(--pillw);
  height: var(--pillw);
  border-radius: 8px;
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
}
/* The bands of the app's position pill: ≤3 success, ≤5 info, ≤10 warning, else error. */
.mkt-pos2-pill.is-good {
  color: #15803D;
  background: rgba(34, 197, 94, 0.12);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, #22C55E 28%, transparent);
}
.mkt-pos2-pill.is-ok {
  color: #1D4ED8;
  background: rgba(59, 130, 246, 0.12);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, #3B82F6 28%, transparent);
}
.mkt-pos2-pill.is-warn {
  color: #B45309;
  background: rgba(245, 158, 11, 0.14);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, #F59E0B 28%, transparent);
}
.mkt-pos2-pill.is-bad {
  color: #B91C1C;
  background: rgba(239, 68, 68, 0.12);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, #EF4444 28%, transparent);
}
/* The flip: the old number is stacked under the new one and only shows while the swap runs. */
.mkt-pos2-flip { display: grid; }
.mkt-pos2-flip b { grid-area: 1 / 1; font-weight: inherit; }
.mkt-pos2-flip .is-prev { opacity: 0; }

/* Wide previews (the single-column 768 layout): everything one notch larger. */
@container (min-width: 620px) {
  /* .mkt-pos2 is the container itself, so its own declarations never match inside these queries:
     the ladder's metrics sit on .mkt-pos2-grid and inherit down to the rows. */
  .mkt-pos2-grid { --namew: 118px; --pillw: 30px; --gap: 8px; --lift: 16.5px; }
  .mkt-pos2-steps { font-size: 13px; }
  .mkt-pos2 .mkt-pos2-model .mkt-llm { width: 17px; height: 17px; }
  .mkt-pos2-name { font-size: 13px; }
  .mkt-pos2-chip { padding: 7px 8px; font-size: 12px; border-radius: 8px; }
  .mkt-pos2-pill { font-size: 14px; border-radius: 9px; }
  .mkt-pos2-scale b { font-size: 10px; }
  .mkt-pos2-grid { row-gap: 6px; }
}

/* Narrower previews (the two-column desktop layout below ~1100px): short competitor names, so
   four rungs still fit without truncating. */
@container (max-width: 519px) {
  .mkt-pos2-grid { --namew: 94px; --pillw: 22px; --gap: 5px; --lift: 11.5px; }
  .mkt-pos2-steps { font-size: 11.5px; }
  .mkt-pos2 .mkt-pos2-model .mkt-llm { width: 13px; height: 13px; }
  .mkt-pos2-name { font-size: 10.5px; }
  .mkt-pos2-chip { padding: 4px 5px; font-size: 9.5px; border-radius: 6px; }
  .mkt-pos2-chip .is-full { display: none; }
  .mkt-pos2-chip .is-short { display: inline; }
  .mkt-pos2-pill { font-size: 11px; border-radius: 7px; }
}

/* The phone column: the ladder and the pills carry the view, and the result line is allowed the two
   lines it needs at this width. */
@container (max-width: 405px) {
  .mkt-pos2-grid { --namew: 78px; --pillw: 20px; --gap: 4px; --lift: 9.5px; row-gap: 3px; }
  .mkt-pos2-steps { font-size: 11px; }
  /* The status line needs both its lines at this width, so the scope chips stand down. */
  .mkt-pos2-filters { display: none; }
  .mkt-pos2-scale b { font-size: 8px; }
  .mkt-pos2 .mkt-pos2-model .mkt-llm { width: 12px; height: 12px; }
  .mkt-pos2-model { gap: 5px; }
  .mkt-pos2-name { font-size: 8.5px; }
  .mkt-pos2-row, .mkt-pos2-scale { column-gap: 6px; }
  .mkt-pos2-chip { padding: 3px 3px; font-size: 8px; }
  .mkt-pos2-pill { font-size: 10px; border-radius: 6px; }
}

/* --- sequence ------------------------------------------------------------------- */

.mkt-pos2.is-armed .mkt-pos2-fchip { animation: pos-rise 0.3s ease calc(0.05s + var(--i) * 0.06s) backwards; }
.mkt-pos2.is-armed .mkt-pos2-scale b { animation: pos-rise 0.3s ease calc(0.05s + var(--r) * 0.05s) backwards; }

/* Nine rows share the 0.2–1.35s window six used to: 0.065s a row lands the last pill at 1.34s. */
.mkt-pos2.is-armed .mkt-pos2-row { animation: pos-fade 0.3s ease calc(0.2s + var(--i) * 0.065s) backwards; }
.mkt-pos2.is-armed .mkt-pos2-model { animation: pos-rise 0.32s ease calc(0.2s + var(--i) * 0.065s) backwards; }
.mkt-pos2.is-armed .mkt-pos2-chip {
  animation: pos-pop 0.34s cubic-bezier(0.2, 0.9, 0.3, 1.25) calc(0.3s + var(--i) * 0.065s + var(--j) * 0.05s) backwards;
}
.mkt-pos2.is-armed .mkt-pos2-pill { animation: pos-pop 0.3s cubic-bezier(0.2, 0.9, 0.3, 1.3) calc(0.52s + var(--i) * 0.065s) backwards; }

/* The fresh ChatGPT answer: Nike comes up one rung, the rival goes down one, and the pill flips.
   The chips already carry their entrance animation, so both are listed in one declaration. */
.mkt-pos2.is-armed .mkt-pos2-chip.is-up {
  animation:
    pos-pop 0.34s cubic-bezier(0.2, 0.9, 0.3, 1.25) calc(0.3s + var(--i) * 0.065s + var(--j) * 0.05s) backwards,
    pos-up 0.58s linear 2.45s backwards;
}
.mkt-pos2.is-armed .mkt-pos2-chip.is-down {
  animation:
    pos-pop 0.34s cubic-bezier(0.2, 0.9, 0.3, 1.25) calc(0.3s + var(--i) * 0.065s + var(--j) * 0.05s) backwards,
    pos-down 0.58s linear 2.45s backwards;
}
/* The lanes already keep the two apart, so this only decides who would win if a future font made
   a chip tall enough to graze the other: the brand rides over the competitor it passes, never
   under it. The chips sit in .mkt-pos2-track, whose row is already positioned, so the stacking
   stays local to the row. */
.mkt-pos2.is-armed .mkt-pos2-chip.is-up { position: relative; z-index: 2; }
.mkt-pos2.is-armed .mkt-pos2-chip.is-down { position: relative; z-index: 1; }
.mkt-pos2.is-armed .mkt-pos2-row.is-move::before { animation: pos-wash 1.15s ease-out 2.4s 1; }
.mkt-pos2.is-armed .mkt-pos2-flip .is-prev { animation: pos-flip-out 0.3s ease 2.86s both; }
.mkt-pos2.is-armed .mkt-pos2-flip .is-now { animation: pos-flip-in 0.3s ease 2.86s both; }

/* The status line hands over to the result it was working towards: every line starts its rise
   0.15s before the one under it has finished fading, so the row always carries something — the
   handover crossfades instead of blinking through an empty frame — and the line that stays is what
   the period came to. */
.mkt-pos2.is-armed .mkt-pos2-spinner { animation: pos-spin 0.7s linear infinite, pos-hold 3.3s linear both; }
.mkt-pos2.is-armed .mkt-pos2-tick { animation: pos-pop 0.34s cubic-bezier(0.2, 0.9, 0.3, 1.3) 3.15s both; }
.mkt-pos2.is-armed .mkt-pos2-steps > .is-read { animation: pos-window 1.35s ease 0s both; }
.mkt-pos2.is-armed .mkt-pos2-steps > .is-compare { animation: pos-window 1.15s ease 1.2s both; }
.mkt-pos2.is-armed .mkt-pos2-steps > .is-fresh { animation: pos-window 1.1s ease 2.2s both; }
.mkt-pos2.is-armed .mkt-pos2-steps > .is-result { animation: pos-rise 0.34s ease 3.15s both; }

/* Waiting for marketing.js to mark the widget live: hold the first frame. */
.mkt-widget:not(.is-live) .mkt-pos2.is-armed *,
.mkt-widget:not(.is-live) .mkt-pos2.is-armed *::after,
.mkt-widget:not(.is-live) .mkt-pos2.is-armed *::before { animation-play-state: paused !important; }

@media (prefers-reduced-motion: reduce) {
  .mkt-pos2 *,
  .mkt-pos2 *::after,
  .mkt-pos2 *::before { animation: none !important; }
}

@keyframes pos-fade { from { opacity: 0; } }
@keyframes pos-rise { from { opacity: 0; translate: 0 4px; } }
@keyframes pos-pop { from { opacity: 0; scale: 0.72; } }
@keyframes pos-window { 0% { opacity: 0; translate: 0 3px; } 12%, 88% { opacity: 1; translate: 0 0; } 100% { opacity: 0; translate: 0 -3px; } }
/* The swap in three beats: the two chips step off the row's baseline into their own lane (one
   above it, one below), slide past each other there, then settle onto the rung they won. They are
   opaque, so the lanes are what keeps them from ever standing on the same pixels: while the
   horizontal leg runs they are 2 x --lift apart, which is more than a chip is tall. */
@keyframes pos-up {
  0% { translate: calc(100% + var(--gap)) 0; animation-timing-function: cubic-bezier(0.32, 0, 0.2, 1); }
  26% { translate: calc(100% + var(--gap)) calc(var(--lift) * -1); animation-timing-function: cubic-bezier(0.5, 0, 0.35, 1); }
  74% { translate: 0 calc(var(--lift) * -1); animation-timing-function: cubic-bezier(0.32, 0, 0.2, 1); }
  100% { translate: 0 0; }
}
@keyframes pos-down {
  0% { translate: calc(-100% - var(--gap)) 0; animation-timing-function: cubic-bezier(0.32, 0, 0.2, 1); }
  26% { translate: calc(-100% - var(--gap)) var(--lift); animation-timing-function: cubic-bezier(0.5, 0, 0.35, 1); }
  74% { translate: 0 var(--lift); animation-timing-function: cubic-bezier(0.32, 0, 0.2, 1); }
  100% { translate: 0 0; }
}
@keyframes pos-wash { from { opacity: 1; } to { opacity: 0; } }
@keyframes pos-flip-out { from { opacity: 1; translate: 0 0; } to { opacity: 0; translate: 0 -80%; } }
@keyframes pos-flip-in { from { opacity: 0; translate: 0 80%; } to { opacity: 1; translate: 0 0; } }
@keyframes pos-spin { to { rotate: 360deg; } }
@keyframes pos-hold { 0%, 91% { opacity: 1; } 100% { opacity: 0; } }

/* === project-switcher ====================================================== */
/*
 * Onboarding · "Wiele marek w jednym koncie" — widgets/v2/project-switcher.blade.php.
 *
 * The app sidebar's project switcher (brand-selector.tsx): the "Projekt" field opens its list over
 * the menu, the agency's four projects show with their state, the pointer travels to Jordan and
 * picks it, and the app follows. The accordion item promises "każdy projekt ma własne śledzenie",
 * so the payoff is the pane behind: /dashboard's VisibilityTeaserCard swaps to Jordan's own
 * tracking — ring, verdict and all three KPIs.
 *
 * Every rule below describes the SETTLED frame (list open, Jordan picked, dashboard on Jordan).
 * The sequence is keyframes whose from-frames hold the earlier state; they apply only once the
 * inline script arms the widget, stay paused until marketing.js marks it live, and restart by
 * themselves each time the accordion shows the widget again (display:none cancels animations).
 *
 * Timeline (s): 0.12 field focus · 0.28 list opens and the menu slides down under it · 0.42–0.66
 * rows land · 1.05–1.95 pointer travels and picks Jordan · 2.15 field switches · 2.35–3.24 the
 * dashboard behind follows.
 *
 * No <section>, no <p> in the markup on purpose — see the note in the blade. marketing.css's
 * `section { padding: 60px !important }` and `p { font-size: 14px !important }` under 640px would
 * override everything here on the block axis and in text size, which is invisible until phone width.
 */

/* Both the counter and the arc are REGISTERED custom properties, and both for the same reason:
   an unregistered custom property has no type, so a declaration that reads one — `calc(100 -
   var(--score-to))` — stays an unresolved calc() at computed-value time and Chromium cannot
   interpolate it: the property flips once, at 50% progress. That is exactly what the arc used to
   do while the digits counted smoothly, so for ~0.55s the ring and the number stated different
   scores in the middle of the payoff. Registering the value the animation actually travels along
   (--ps-arc, a number of the 100-unit pathLength) makes stroke-dashoffset follow it every frame. */
@property --ps-n { syntax: '<integer>'; inherits: false; initial-value: 0; }
@property --ps-arc { syntax: '<number>'; inherits: false; initial-value: 100; }

.mkt-ps {
  container-type: inline-size;
  gap: 0;
}

.mkt-ps-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- app shell ---------------------------------------------------------------- */

.mkt-ps-app {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 46%) minmax(0, 1fr);
  /* minmax(0, …): without it a column refuses to shrink below its content and the pane grows
     past the frame, clipping the card. */
  grid-template-rows: minmax(0, 1fr);
  border: 1px solid #EDEEF2;
  border-radius: 12px;
  background: #FFFFFF;
  overflow: hidden;
}

.mkt-ps-side {
  --ps-row-h: clamp(23px, 6.2cqw, 30px);
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 2.2cqw;
  min-width: 0;
  min-height: 0;
  padding: 2.6cqw 2.4cqw;
  border-right: 1px solid #EDEEF2;
  background: #FAFAFC;
}

/* The menu is taller than the frame on purpose — a real sidebar continues below the fold. Without
   this the last item is guillotined flush against the border and reads as a bug, so the bottom
   strip dissolves into the sidebar instead. Under the list (z-index 3), over the nav. */
.mkt-ps-side::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  height: clamp(20px, 5.5cqw, 34px);
  background: linear-gradient(to bottom, rgba(250, 250, 252, 0) 0%, #FAFAFC 78%);
  pointer-events: none;
}

.mkt-ps-logo svg {
  display: block;
  width: auto;
  height: clamp(13px, 3.4cqw, 17px);
}

/* --- the switcher ------------------------------------------------------------- */

.mkt-ps-picker {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.9cqw;
  min-width: 0;
}

.mkt-ps-overline {
  font-family: 'IBM Plex Mono', monospace;
  font-size: clamp(6.5px, 1.75cqw, 9px);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #656B7E;
}

/* The field keeps the focused border while its list is open. */
.mkt-ps-field {
  display: flex;
  align-items: center;
  gap: 1.5cqw;
  min-width: 0;
  padding: 1.5cqw 1.8cqw;
  border: 1px solid #5B5CE6;
  border-radius: 9px;
  background: #FFFFFF;
  box-shadow: 0 0 0 2.5px rgba(91, 92, 230, 0.14);
}

/* Main brand logo on white with a hairline ring — ProjectSwitcherRailIcon / BrandSelector. */
.mkt-ps-mark {
  position: relative;
  display: grid;
  place-items: center;
  width: clamp(16px, 4.4cqw, 21px);
  height: clamp(16px, 4.4cqw, 21px);
  flex: 0 0 auto;
  border-radius: 5px;
  background: #FFFFFF;
  box-shadow: inset 0 0 0 1px #E4E6EE;
}
/* The tile is the full box; the WIDTH comes from the mark itself, inline, per brand — the same
   numbers partials/marketing/widgets/brand-mark.blade.php uses, so the swoosh and the Jumpman
   carry the same optical weight here as they do in the onboarding previews. */
.mkt-ps-mark-in {
  grid-area: 1 / 1;
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
}
.mkt-ps-mark-in svg { display: block; max-width: 100%; max-height: 82%; }
.mkt-ps-mark-in.is-from { opacity: 0; }

.mkt-ps-field-name {
  display: grid;
  flex: 1;
  min-width: 0;
  font-size: clamp(9.5px, 2.7cqw, 13.5px);
  font-weight: 600;
  color: #14162B;
}
.mkt-ps-field-name > span {
  grid-area: 1 / 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.mkt-ps-field-name > .is-from { opacity: 0; }

.mkt-ps-caret {
  width: clamp(9px, 2.5cqw, 12px);
  height: clamp(9px, 2.5cqw, 12px);
  flex: 0 0 auto;
  color: #8B90A3;
  rotate: 180deg;
}

/* The list floats over the menu, like the Autocomplete listbox. */
.mkt-ps-list {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 3;
  margin-top: 1cqw;
  padding: 1cqw;
  border: 1px solid #E4E6EE;
  border-radius: 10px;
  background: #FFFFFF;
  box-shadow: 0 18px 34px -20px rgba(20, 22, 43, 0.5);
  transform-origin: 50% 0;
}

.mkt-ps-cursor {
  position: absolute;
  left: 1cqw;
  right: 1cqw;
  top: 1cqw;
  height: var(--ps-row-h);
  border-radius: 7px;
  background: rgba(91, 92, 230, 0.08);
  opacity: 0;
  pointer-events: none;
}

.mkt-ps-opt {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 1.4cqw;
  height: var(--ps-row-h);
  padding: 0 1.4cqw;
  border-radius: 7px;
  font-size: clamp(8.5px, 2.4cqw, 12px);
  color: #14162B;
}
.mkt-ps-opt.is-picked {
  background: rgba(91, 92, 230, 0.12);
  font-weight: 600;
}

.mkt-ps-opt-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.mkt-ps-radio {
  position: relative;
  width: clamp(9px, 2.5cqw, 12px);
  height: clamp(9px, 2.5cqw, 12px);
  flex: 0 0 auto;
  border-radius: 999px;
  box-shadow: inset 0 0 0 1.4px #C4C8D6;
}
.mkt-ps-opt.is-picked .mkt-ps-radio { box-shadow: inset 0 0 0 1.4px #5B5CE6, inset 0 0 0 3.2px #5B5CE6, inset 0 0 0 4.4px #FFFFFF; }

/* isProjectPaused → the outlined "Wstrzymany" chip. */
.mkt-ps-chip {
  flex: 0 0 auto;
  padding: 0.25em 0.6em;
  border: 1px solid #D5D8E2;
  border-radius: 999px;
  font-size: 0.82em;
  font-weight: 500;
  line-height: 1.5;
  color: #7C8196;
  white-space: nowrap;
}

.mkt-ps-divider {
  display: block;
  height: 1px;
  margin: 1cqw 1.4cqw;
  background: #EDEEF2;
}

.mkt-ps-add {
  display: flex;
  align-items: center;
  gap: 1.4cqw;
  height: var(--ps-row-h);
  padding: 0 1.4cqw;
  border-radius: 7px;
  font-size: clamp(8.5px, 2.4cqw, 12px);
  font-weight: 600;
  color: #16A34A;
}
.mkt-ps-plus { display: grid; place-items: center; flex: 0 0 auto; }
.mkt-ps-plus svg { width: clamp(10px, 2.7cqw, 13px); height: clamp(10px, 2.7cqw, 13px); display: block; }

/* --- menu behind the list ------------------------------------------------------ */

/* The open list floats over the menu, so the menu starts below it: items emerge whole rather
   than sliced in half by the list's bottom edge. The reserve is the list's own height measured
   from the field: five rows (four projects + "Dodaj projekt"), the list's 1cqw padding top and
   bottom, the divider's 1cqw margins, its 1px rule, the panel's 2px of border and the 1cqw it
   hangs below the field — less the 2.2cqw the sidebar already puts between the picker and the
   menu. Counting only three of those cqw left the panel 1.8px ON TOP of the active Dashboard
   pill, flattening its top corners; the extra cqw clears it and leaves ~1.2cqw of daylight, so
   the list reads as floating over the menu instead of grazing it. */
.mkt-ps-nav {
  display: flex;
  flex-direction: column;
  gap: 0.4cqw;
  min-width: 0;
  padding-top: calc(var(--ps-row-h) * 5 + 4cqw + 3px);
}
.mkt-ps-nav-item {
  display: flex;
  align-items: center;
  gap: 1.4cqw;
  padding: 1.1cqw 1.4cqw;
  border-radius: 7px;
  font-size: clamp(8px, 2.2cqw, 11px);
  color: #5F6478;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mkt-ps-nav-item svg { width: clamp(10px, 2.8cqw, 14px); height: clamp(10px, 2.8cqw, 14px); flex: 0 0 auto; }
.mkt-ps-nav-item.is-on { background: rgba(91, 92, 230, 0.10); color: #4B4CD8; font-weight: 600; }

/* --- the dashboard the switch lands on ----------------------------------------- */

.mkt-ps-content {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 2cqw;
  min-width: 0;
  min-height: 0;
  padding: 2.6cqw 2.4cqw;
  overflow: hidden;
}

.mkt-ps-sweep {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: linear-gradient(100deg, transparent 38%, rgba(91, 92, 230, 0.09) 50%, transparent 62%);
  opacity: 0;
  pointer-events: none;
}

.mkt-ps-head { display: flex; flex-direction: column; gap: 0.6cqw; min-width: 0; flex: 0 0 auto; }
.mkt-ps-crumb {
  font-family: 'IBM Plex Mono', monospace;
  font-size: clamp(6.5px, 1.75cqw, 9px);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #656B7E;
}
.mkt-ps-title {
  display: grid;
  min-width: 0;
  font-size: clamp(11px, 3.2cqw, 16px);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: #14162B;
}
.mkt-ps-title > span {
  grid-area: 1 / 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.mkt-ps-title > .is-from { opacity: 0; }

/* ProjectHomeTeaserCard: header with a "Szczegóły" action, then the content. */
.mkt-ps-card {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: 1.6cqw;
  min-width: 0;
  min-height: 0;
  padding: 2cqw 2.1cqw;
  border: 1px solid #EDEEF2;
  border-radius: 10px;
  background: #FCFCFE;
}

.mkt-ps-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.4cqw;
  min-width: 0;
  flex: 0 0 auto;
}
.mkt-ps-card-title {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: clamp(8.5px, 2.4cqw, 12.5px);
  font-weight: 600;
  color: #14162B;
}
.mkt-ps-card-link {
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  flex: 0 0 auto;
  font-size: clamp(7px, 1.95cqw, 10px);
  font-weight: 600;
  color: #5B5CE6;
  white-space: nowrap;
}
.mkt-ps-card-link svg { width: 1em; height: 1em; display: block; }

/* The card is always taller than the type it holds, because every size in it is clamped: on the
   widest preview (a 702px container at the 768 viewport) the ring, the sentence and the labels
   have all hit their ceilings, on the phone several have hit their floors. Whoever is allowed to
   GROW absorbs that slack, and this used to be the body — so the leftover collected under the
   verdict as a dead band, 18% of the card at 390 and 15% at 768 but only 4% at 1440: a hole, and
   a different hole at every width. Centring the body instead only split it into two voids (tried;
   ~27% of the card at 768). So the body now sizes to its content and the KPI tiles take the slack
   into their own height, where it lands as padding around the label and the number — air, and the
   same air at every width. Grow but never shrink (1 0 auto): when the frame is TIGHT instead, the
   tiles must keep their size and the body must give, exactly as before. */
.mkt-ps-cardbody {
  display: flex;
  flex: 0 1 auto;
  flex-direction: column;
  justify-content: flex-start;
  gap: 1.5cqw;
  min-width: 0;
  min-height: 0;
}

/* ScoreCircle, size 72 in the app: the ring, the value, and the change beside it. */
.mkt-ps-gauge {
  display: flex;
  align-items: center;
  gap: 1.6cqw;
  min-width: 0;
  flex: 0 0 auto;
}
/* ScoreCircle is size={72} in the app. The cap is that 72 — the preview box narrows again above a
   1000px viewport (body zoom .9), so the ring only reaches it on the widest previews and scales
   down with the container elsewhere rather than capping early and floating in a tall card. */
.mkt-ps-ring {
  position: relative;
  width: clamp(34px, 12.5cqw, 72px);
  height: clamp(34px, 12.5cqw, 72px);
  flex: 0 0 auto;
}
.mkt-ps-ring svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
.mkt-ps-ring circle { fill: none; stroke-width: 4; }
.mkt-ps-ring .is-track { stroke: #EDEEF2; }
/* Jordan's 54 sits in ScoreCircle's amber band; the keyframe holds Nike's green 72. */
.mkt-ps-ring .is-value {
  --ps-arc: calc(100 - var(--score-to));
  stroke: #F59E0B;
  stroke-linecap: round;
  stroke-dasharray: 100;
  stroke-dashoffset: var(--ps-arc);
}
.mkt-ps-num {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  --ps-n: var(--score-to);
  counter-reset: ps var(--ps-n);
  /* labelFontSize={22} against size={72} — the same 0.3 ratio, held as the ring scales. */
  font-size: clamp(12px, 4cqw, 22px);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: #14162B;
}
.mkt-ps-num::after { content: counter(ps); }

/* The change beside the score is the site-wide .mkt-delta (marketing.css:1050) — the same ↑/↓,
   the same unsigned number and the same green/red that widgets/v2/visibility-score prints beside
   the very same Nike 72 one accordion step away. This widget used to spell it "▲ +4": a second
   arrow glyph and a second sign convention for one datum on one page. Only the size is re-taken
   here, because .mkt-delta is fixed at 11px and everything in this widget scales with cqw. */
.mkt-ps-delta {
  display: grid;
  flex: 0 0 auto;
}
.mkt-ps-delta > span {
  grid-area: 1 / 1;
  font-size: clamp(7px, 1.95cqw, 10px);
  white-space: nowrap;
}
.mkt-ps-delta > .is-from { opacity: 0; }

/* visibilityAssessmentKey(): the sentence for the score's band, so the verdict moves with it. */
.mkt-ps-assess {
  display: grid;
  margin: 0;
  flex: 0 1 auto;
  min-height: 0;
  overflow: hidden;
  font-size: clamp(7.5px, 2.15cqw, 13px);
  line-height: 1.5;
  color: #656B7E;
  text-wrap: pretty;
}
.mkt-ps-assess > span { grid-area: 1 / 1; }
.mkt-ps-assess > .is-from { opacity: 0; }

/* kpiItems at kpiColumns === 1: each metric is its own Card — `flex flex-row items-center gap-2.5
   px-3 py-2` with a 28px tinted icon box, the label, then the value. Bare rows under a divider
   left ~105px of the card empty; the real tiles fill it and match the app. */
.mkt-ps-kpis {
  display: flex;
  flex-direction: column;
  gap: 1.1cqw;
  margin: 0;
  flex: 1 0 auto;
}
.mkt-ps-kpis > div {
  display: flex;
  flex: 1 0 auto;
  align-items: center;
  gap: 1.2cqw;
  min-width: 0;
  padding: 0.9cqw 1.2cqw;
  border: 1px solid #EDEEF2;
  border-radius: 8px;
  background: #FFFFFF;
}
/* h-7 w-7 rounded-md bg-{color}/10 with the MappedIcon in {color}. */
.mkt-ps-kpi-ico {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: clamp(14px, 4.1cqw, 28px);
  height: clamp(14px, 4.1cqw, 28px);
  border-radius: 6px;
  background: rgba(34, 197, 94, 0.12);
  color: #16A34A;
}
.mkt-ps-kpi-ico svg { display: block; width: 62%; height: 62%; }
.mkt-ps-kpis > .is-info .mkt-ps-kpi-ico { background: rgba(91, 92, 230, 0.12); color: #5B5CE6; }
.mkt-ps-kpis > .is-warn .mkt-ps-kpi-ico { background: rgba(245, 158, 11, 0.14); color: #D97706; }
.mkt-ps-kpis dt {
  display: flex;
  align-items: center;
  gap: 1.2cqw;
  flex: 1;
  min-width: 0;
}
.mkt-ps-kpi-lbl {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-family: 'IBM Plex Mono', monospace;
  font-size: clamp(6.5px, 1.8cqw, 10.5px);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #656B7E;
}
.mkt-ps-kpis dd {
  display: grid;
  margin: 0;
  flex: 0 0 auto;
  font-size: clamp(9.5px, 2.8cqw, 16px);
  font-weight: 600;
  line-height: 1.15;
  color: #14162B;
}
.mkt-ps-kpis dd > span { grid-area: 1 / 1; white-space: nowrap; text-align: right; }
.mkt-ps-kpis dd > .is-from { opacity: 0; }

/* Mid-range previews (the 1024 one is a ~456px container): the chip is sized in ems off the row
   and never shrinks, so as the column narrows the project NAME absorbs all of the loss — the one
   row carrying a state was the one truncated. Give the chip its ~10px back. */
@container (max-width: 470px) {
  .mkt-ps-chip { font-size: 0.74em; padding: 0.2em 0.45em; }
  .mkt-ps-opt { gap: 1.1cqw; padding: 0 1.1cqw; }
}

/* The 1024 preview is the tightest frame of the five: a 456px container on a 5/4 box leaves the
   card ~228px for head + ring + verdict + three KPI tiles, and the verdict — the one thing that
   must stay readable, since it is the sentence the switch rewrites — was overflowing its box by
   9px, guillotined through the middle of its last line. The room comes from the gaps and the
   pane's padding rather than from any row: nothing is dropped and nothing is squeezed to a new
   size, the frame just stops holding its breath. Above 470px (768/1440/1920) and on the phone
   the sentence already fits, so the band is closed at both ends. */
@container (min-width: 400.02px) and (max-width: 470px) {
  .mkt-ps-content { padding: 2.25cqw 2.4cqw; gap: 1.6cqw; }
  .mkt-ps-card { gap: 1.35cqw; }
  .mkt-ps-cardbody { gap: 1.2cqw; }
  .mkt-ps-kpis { gap: 0.95cqw; }
  .mkt-ps-ring { width: clamp(34px, 11.8cqw, 72px); height: clamp(34px, 11.8cqw, 72px); }
}

/* Phone-width previews: the switcher needs the room, the dashboard behind gives it up. */
@container (max-width: 400px) {
  .mkt-ps-app { grid-template-columns: minmax(0, 52%) minmax(0, 1fr); }
  .mkt-ps-card { padding: 1.6cqw 1.7cqw; gap: 1.2cqw; }
  .mkt-ps-card-link span,
  .mkt-ps-card-link { font-size: 0; gap: 0; }
  .mkt-ps-card-link svg { width: clamp(8px, 2.4cqw, 11px); height: clamp(8px, 2.4cqw, 11px); }
  .mkt-ps-gauge { gap: 1.2cqw; }
  .mkt-ps-kpis { gap: 0.8cqw; }
  .mkt-ps-kpis > div { padding: 0.7cqw 1cqw; gap: 1cqw; }
  .mkt-ps-kpis dt { gap: 1cqw; }
  .mkt-ps-nav-item { padding: 0.9cqw 1.2cqw; }
}

/* --- sequence ------------------------------------------------------------------ */

.mkt-ps.is-armed .mkt-ps-field { animation: ps-field-open 0.26s ease 0.12s backwards, ps-flash 0.5s ease 2.12s; }
.mkt-ps.is-armed .mkt-ps-caret { animation: ps-caret 0.3s cubic-bezier(0.22, 1, 0.36, 1) 0.16s backwards; }
.mkt-ps.is-armed .mkt-ps-list { animation: ps-open 0.32s cubic-bezier(0.22, 1, 0.36, 1) 0.28s backwards; }
.mkt-ps.is-armed .mkt-ps-opt,
.mkt-ps.is-armed .mkt-ps-divider,
.mkt-ps.is-armed .mkt-ps-add { animation: ps-row 0.28s ease calc(0.42s + var(--r) * 0.06s) backwards; }

/* The nav permanently reserves the list's height so items are never sliced by its bottom edge —
   but before the list opens that reserve is just a hole in the sidebar, and the held first frame
   is the frame people look at longest. So the menu starts pulled up into the hole and slides down
   exactly as the list expands over it. Transform only: the settled layout keeps its reserve, so
   the no-JS and reduced-motion frames are unaffected. */
.mkt-ps.is-armed .mkt-ps-nav { animation: ps-nav-settle 0.34s cubic-bezier(0.22, 1, 0.36, 1) 0.28s backwards; }

.mkt-ps.is-armed .mkt-ps-cursor { animation: ps-cursor 0.9s cubic-bezier(0.4, 0, 0.2, 1) 1.05s both; }
.mkt-ps.is-armed .mkt-ps-opt.is-picked { animation: ps-pick 0.28s ease 1.95s backwards; }
.mkt-ps.is-armed .mkt-ps-opt.is-picked .mkt-ps-radio { animation: ps-radio-on 0.32s cubic-bezier(0.2, 0.9, 0.3, 1.35) 1.95s backwards; }
.mkt-ps.is-armed .mkt-ps-opt.is-current { animation: ps-unpick 0.28s ease 2.03s backwards; }
.mkt-ps.is-armed .mkt-ps-opt.is-current .mkt-ps-radio { animation: ps-radio-off 0.28s ease 2.03s backwards; }

/* Every swap crossfades: the new value starts 0.11s after the old one begins leaving, so the two
   overlap for ~0.09s. Running them strictly end to end (out finishes, THEN in starts) left the
   element EMPTY for ~100ms — the field rendered as a blank outlined control at 2.29–2.39s, i.e.
   an emptied input at the exact moment the switch is meant to land, which reads as "cleared",
   not "switching". The overlap is deliberately lopsided: the outgoing value is linear and short
   (0.2s), so it is already under ~0.3 opacity by the time the incoming one becomes legible, while
   the incoming eases in fast. Symmetric fades put both near 0.5 at the crossing and ghosted into
   visible double text. Both spans share one grid cell and roll on their own em, so what is left
   reads as one value replacing another. Verified: no pair ever sits below 0.25 together. */
.mkt-ps.is-armed .mkt-ps-mark-in.is-from,
.mkt-ps.is-armed .mkt-ps-field-name > .is-from { animation: ps-out 0.2s linear 2.15s both; }
.mkt-ps.is-armed .mkt-ps-mark-in.is-to,
.mkt-ps.is-armed .mkt-ps-field-name > .is-to { animation: ps-in 0.26s cubic-bezier(0.22, 1, 0.36, 1) 2.26s backwards; }

/* backwards, not both: "both" parks the sweep on its last keyframe, translated out of the pane. */
.mkt-ps.is-armed .mkt-ps-sweep { animation: ps-sweep 0.7s ease-out 2.35s backwards; }
.mkt-ps.is-armed .mkt-ps-title > .is-from { animation: ps-out 0.2s linear 2.45s both; }
.mkt-ps.is-armed .mkt-ps-title > .is-to { animation: ps-in 0.26s cubic-bezier(0.22, 1, 0.36, 1) 2.56s backwards; }

/* One element travels from Nike's arc to Jordan's — crossfading two rings left a half-drawn
   frame in the middle of the payoff. */
.mkt-ps.is-armed .mkt-ps-ring .is-value { animation: ps-ring 0.62s cubic-bezier(0.22, 1, 0.36, 1) 2.62s backwards; }
.mkt-ps.is-armed .mkt-ps-num { animation: ps-count 0.62s cubic-bezier(0.22, 1, 0.36, 1) 2.62s backwards; }
.mkt-ps.is-armed .mkt-ps-delta > .is-from { animation: ps-out 0.2s linear 2.62s both; }
.mkt-ps.is-armed .mkt-ps-delta > .is-to { animation: ps-in 0.26s cubic-bezier(0.22, 1, 0.36, 1) 2.73s backwards; }

.mkt-ps.is-armed .mkt-ps-assess > .is-from { animation: ps-out 0.2s linear 2.68s both; }
.mkt-ps.is-armed .mkt-ps-assess > .is-to { animation: ps-in 0.26s cubic-bezier(0.22, 1, 0.36, 1) 2.79s backwards; }

.mkt-ps.is-armed .mkt-ps-kpis dd > .is-from { animation: ps-out 0.2s linear calc(2.72s + var(--r) * 0.07s) both; }
.mkt-ps.is-armed .mkt-ps-kpis dd > .is-to { animation: ps-in 0.26s cubic-bezier(0.22, 1, 0.36, 1) calc(2.83s + var(--r) * 0.07s) backwards; }

/* Waiting for marketing.js to mark the widget live: hold the first frame. */
.mkt-widget:not(.is-live) .mkt-ps.is-armed *,
.mkt-widget:not(.is-live) .mkt-ps.is-armed *::after,
.mkt-widget:not(.is-live) .mkt-ps.is-armed *::before { animation-play-state: paused !important; }

@media (prefers-reduced-motion: reduce) {
  .mkt-ps *,
  .mkt-ps *::after,
  .mkt-ps *::before { animation: none !important; }
}

@keyframes ps-field-open { from { border-color: #DEE0E8; box-shadow: none; } }
@keyframes ps-caret { from { rotate: 0deg; } }
/* Opacity lands early: a half-open list that is still translucent shows the menu through it. */
@keyframes ps-open {
  from { clip-path: inset(0 0 100% 0); opacity: 0; scale: 1 0.96; }
  28% { opacity: 1; }
}
@keyframes ps-row { from { opacity: 0; translate: 0 -4px; } }
@keyframes ps-cursor {
  0% { opacity: 0; translate: 0 0; }
  14% { opacity: 1; translate: 0 0; }
  58%, 72% { opacity: 1; translate: 0 calc(var(--ps-row-h) * 2); }
  100% { opacity: 0; translate: 0 calc(var(--ps-row-h) * 2); }
}
@keyframes ps-pick { from { background: transparent; } }
@keyframes ps-radio-on {
  from { box-shadow: inset 0 0 0 1.4px #C4C8D6; scale: 1; }
  55% { scale: 1.3; }
  to { scale: 1; }
}
@keyframes ps-unpick { from { background: rgba(91, 92, 230, 0.12); } }
@keyframes ps-radio-off { from { box-shadow: inset 0 0 0 1.4px #5B5CE6, inset 0 0 0 3.2px #5B5CE6, inset 0 0 0 4.4px #FFFFFF; } }
@keyframes ps-flash { 0%, 100% { background: #FFFFFF; } 45% { background: #F4F5FE; } }
/* em, not px: the roll scales with the text it moves, so the swap reads the same on the 8px phone
   label and the 22px score. */
@keyframes ps-out { from { opacity: 1; translate: 0 0; } to { opacity: 0; translate: 0 -0.42em; } }
@keyframes ps-in { from { opacity: 0; translate: 0 0.5em; } }
/* Kept in step with .mkt-ps-nav's padding-top: the menu starts pulled up by exactly the reserve. */
@keyframes ps-nav-settle { from { translate: 0 calc((var(--ps-row-h) * 5 + 4cqw + 3px) * -1); } }
@keyframes ps-sweep { 0% { opacity: 0; translate: -40% 0; } 35% { opacity: 1; } 100% { opacity: 0; translate: 40% 0; } }
@keyframes ps-ring { from { stroke: #22C55E; --ps-arc: calc(100 - var(--score-from)); } }
@keyframes ps-count { from { --ps-n: var(--score-from); } }

/* === query-list ============================================================ */
/*
 * Product page preview — Monitoring → "Śledzone zapytania" (widgets/v2/query-list.blade.php).
 *
 * Every rule below describes the FINISHED list: the four summary boxes of the query dashboard and
 * five rows of the tracked-queries table (query, the brand's position, the mentions and the models
 * that name it). The sequence that leads there is a set of keyframes whose early frames hold the
 * earlier states. They apply only once the widget is armed by its inline script, sit paused until
 * marketing.js marks the widget live, and restart by themselves each time the accordion shows the
 * widget again (display: none cancels CSS animations).
 *
 * The tiles are the SAME component as the tiles above the chart in the sibling Monitoring preview
 * (.mkt-vs-kpi): a separate white box, 1px border, 10px radius, a mono uppercase label and a
 * number — no dots, no change chips, no colour. They also switch size on the SAME width buckets
 * the sibling uses (620 / 470 / 430), not on this widget's own height buckets, or the two rows of
 * boxes would be a different size beside each other in the 1024 column. Four of them fit above
 * 470px; at 470 and below the row drops "Śledzone" and shows three, which is the count the sibling
 * carries at every width — a label is never shortened to keep a box. The two filter chips are
 * the citations preview's chips word for word and pixel for pixel. That consistency is the point:
 * the boxes over the table have to read the same in every animation.
 *
 * Timeline (s): 0–0.3 the chips rise and the tiles come up with loaders in their value slots
 *               · 0.1 the column head and the empty table under it · 0.4–1.35 the five rows land
 *               in order, each one popping its position pill, counting its mentions and lighting
 *               the models that name the brand · 1.04 the first status sentence rolls out as the
 *               second rolls in · 1.5 the loaders clear and the tiles count up — the summary of
 *               what just landed · 1.80 the status line and its spinner are gone · 2.36 the last
 *               tile stops counting and the frame is the settled one.
 * The two status sentences hand the slot over on ramps that overlap by about 0.1s, one rolling up
 * and out as the other rolls in: the slot never drops below about half its ink, so the handover
 * reads as a roll rather than a blink, and the two are never both legible.
 *
 * There is deliberately no third, "Monitoring zaktualizowany" status line, no green tick after the
 * narration and no closing sentence under the table: a success alert is not a result, and a
 * wordless tick is the same alert drawn as a glyph — with the sentences gone it would sit in the
 * slot alone. The settled frame carries NO status line and no loader; their resting state is
 * invisible, so the `both` fill lands exactly where the no-JS and reduced-motion frames already
 * are. Nothing narrates a run that is over.
 *
 * Sizing. The widget body is the query container (container-type: size). The table scales with the
 * height it is given and the tiles with the width, with one cross-check: a preview that is tall but
 * narrow (a ~490px container) steps the table type back down, or the tall branch's 14.5px runs the
 * longest query into the ellipsis. Two consequences drive the structure below:
 *   1. An element cannot query itself, so every container-query-driven declaration lives on a
 *      DESCENDANT — the column template and row height sit on .mkt-ql-table, never on .mkt-ql.
 *   2. Queries resolve against the container's CONTENT box, i.e. the preview's box minus this
 *      body's padding, in CSS px (the page's 0.9 body zoom does not enter the query). Measured on
 *      /pl/produkt: 320x279 at 390, 662x447 at 768, 467x300 at 1024, 558x368 at 1440 and 1920 —
 *      hence the 400/320px height buckets for the table and the 620/470/430px width buckets.
 * Five rows is the teaser, not a page of the table, and they SHARE the height the preview has
 * left instead of sitting in a short stack over a hole: .mkt-ql-table, .mkt-ql-rows and every row
 * grow, so the air of a tall preview falls between the rows. --row-h is the floor they never go
 * under; the table keeps overflow: hidden as a safety net if a preview is shorter still. The last
 * row — the query no model mentions — is what the list is read for, so it is never shed.
 *
 * Class prefix mkt-ql-. The only shared class reused is .mkt-llm (model logo), and it is only ever
 * overridden under .mkt-ql.
 */

@property --ql-n { syntax: '<integer>'; inherits: false; initial-value: 0; }

.mkt-ql {
  container-type: size;
  gap: 9px;
  --ink: #14162B;
  --muted: #656B7E;
  --faint: #9AA0B4;
  --line: #EDEEF2;
  --brand: #5B5CE6;
  --ok: #22C55E;
  --warn: #F59E0B;
  --bad: #EF4444;
}

.mkt-ql-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Counters: the integer sits in --to, the real number in the visually hidden span, so a blocked
   stylesheet still reads correctly. */
.mkt-ql-num { --ql-n: var(--to); counter-reset: qln var(--ql-n); }
.mkt-ql-num::after { content: counter(qln); }

/* --- status line + filter chips ------------------------------------------------
 * The chips are the settled state of this row; the spinner, the tick and the three status lines
 * are all invisible at rest and only the sequence brings them in — and takes them away again. */

.mkt-ql-top { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; min-height: 17px; }
.mkt-ql-state { position: relative; width: 14px; height: 14px; flex: 0 0 auto; }
.mkt-ql-spinner {
  position: absolute;
  inset: 0;
  /* border-box, or the 2px ring would make the spinner 18px where the tick that replaces it is
     14px — the swap would jump, and the ring would bleed into the body's padding. */
  box-sizing: border-box;
  border-radius: 50%;
  border: 2px solid #E3E4FA;
  border-top-color: var(--brand);
  opacity: 0;
}
.mkt-ql-steps { display: grid; flex: 1 1 auto; min-width: 0; font-size: 12.5px; font-weight: 500; color: #262A47; }
.mkt-ql-steps > span {
  grid-area: 1 / 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  opacity: 0;
}
.mkt-ql-filters { display: flex; gap: 6px; flex: 0 0 auto; margin-left: auto; }
.mkt-ql-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border: 1px solid #E3E5EC;
  border-radius: 999px;
  background: #FFFFFF;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px;
  color: #4C5163;
  white-space: nowrap;
}
.mkt-ql-chip::after {
  content: "";
  width: 4px;
  height: 4px;
  border: solid #9AA0B4;
  border-width: 0 1.2px 1.2px 0;
  transform: translateY(-1px) rotate(45deg);
}

/* --- summary tiles --------------------------------------------------------------
 * The stat cards of the query dashboard, in the height a preview has: a label, a number, and a
 * loader in the value slot until the number lands. Same box as the sibling Monitoring previews. */

.mkt-ql-stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
  margin: 0;
  flex: 0 0 auto;
}
.mkt-ql-stat {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  padding: 8px 10px 9px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #FFFFFF;
}
.mkt-ql-stat dt {
  min-width: 0;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8.5px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mkt-ql-stat dd {
  position: relative;
  display: flex;
  align-items: baseline;
  gap: 4px;
  margin: 0;
  min-width: 0;
}
.mkt-ql-stat-val {
  display: inline-flex;
  align-items: baseline;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.mkt-ql-stat-val em { font-style: normal; }
/* The loader the app shows while a stat card is still coming in: visible only while the run is
   going, so the settled frame (and a frame without animation) never shows it. */
.mkt-ql-skel {
  position: absolute;
  left: 0;
  top: 50%;
  translate: 0 -50%;
  width: 72%;
  height: 11px;
  border-radius: 3px;
  background: linear-gradient(90deg, #ECEEF3 0%, #F7F8FB 50%, #ECEEF3 100%);
  background-size: 220% 100%;
  opacity: 0;
  pointer-events: none;
}

/* --- tracked queries table ------------------------------------------------------
 * The column template and row height live here, not on .mkt-ql: .mkt-ql is the query container and
 * an element cannot query itself. */

.mkt-ql-table {
  display: flex;
  flex-direction: column;
  /* Takes every pixel the tiles and the status row leave, so the list never sits over a hole. */
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  /* query · position · mentions · models. The models track is explicit — nine marks and eight
     gaps — for the same reason as in the citations table: the header and each row are separate
     grids, and an `auto` track resolved to "MODELE" in one and to the marks in the other, which
     put the header labels off their columns once the strip grew to nine marks. */
  --mark: 12px;
  --mgap: 3px;
  --marks: calc(9 * var(--mark) + 8 * var(--mgap));
  --cols: minmax(0, 1fr) 30px 58px var(--marks);
  --gap: 8px;
  --row-h: 28px;
}
.mkt-ql-head,
.mkt-ql-row {
  display: grid;
  grid-template-columns: var(--cols);
  align-items: center;
  column-gap: var(--gap);
}
.mkt-ql-head {
  flex: 0 0 auto;
  padding: 0 6px 5px;
  border-bottom: 1px solid var(--line);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--faint);
  white-space: nowrap;
}
.mkt-ql-head .is-pos,
.mkt-ql-head .is-n,
.mkt-ql-head .is-m { text-align: center; }
.mkt-ql-head .is-q,
.mkt-ql-head .is-n { overflow: hidden; text-overflow: ellipsis; }

/* The five rows share whatever height is left: fewer rows with more air, rather than a short
   stack with a hole under it. --row-h is the floor, never the height — a preview shorter than
   five floors clips inside the table's overflow instead of squeezing the type. */
.mkt-ql-rows { position: relative; display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
/* The empty table is drawn before anything lands in it — four hairlines where the five rows will
   divide — so the first beat is a view rather than a white hole, and it is gone by the time the
   last row arrives. The percentages track the rows, which share the height, so the lines sit on
   the real dividers at every preview size. */
.mkt-ql-rows::before {
  content: "";
  position: absolute;
  inset: 0 0 calc(100% / 5) 0;
  opacity: 0;
  pointer-events: none;
  background-image: repeating-linear-gradient(
    to bottom,
    transparent 0,
    transparent calc(100% / 4 - 1px),
    #F1F2F7 calc(100% / 4 - 1px),
    #F1F2F7 calc(100% / 4)
  );
}
.mkt-ql-row {
  position: relative;
  flex: 1 1 auto;
  padding: 0 6px;
  min-height: var(--row-h);
  border-radius: 6px;
  font-size: 11.5px;
  color: var(--ink);
}
.mkt-ql-row + .mkt-ql-row { box-shadow: inset 0 1px 0 #F4F5F8; }
/* The query no model answers with: the gap this view is read for, and the row the "Bez wzmianki"
   tile counts. The tint, the "–" pill and the empty bar carry the miss. */
.mkt-ql-row.is-gap { background: #FEF6F6; box-shadow: none; }

.mkt-ql-row .is-q {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mkt-ql-row.is-gap .is-q { color: #8A8FA3; }

/* Position: the bands of the app's position pill — ≤3 success, ≤5 info, ≤10 warning, no position
   at all stays neutral. */
.mkt-ql-row .is-pos { display: flex; justify-content: center; }
.mkt-ql-pill {
  display: grid;
  place-items: center;
  min-width: 22px;
  height: 17px;
  padding: 0 4px;
  border-radius: 6px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.mkt-ql-pill.is-good {
  color: #15803D;
  background: rgba(34, 197, 94, 0.12);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, #22C55E 28%, transparent);
}
.mkt-ql-pill.is-ok {
  color: #1D4ED8;
  background: rgba(59, 130, 246, 0.12);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, #3B82F6 28%, transparent);
}
.mkt-ql-pill.is-warn {
  color: #B45309;
  background: rgba(245, 158, 11, 0.14);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, #F59E0B 28%, transparent);
}
.mkt-ql-pill.is-bad {
  color: #B91C1C;
  background: rgba(239, 68, 68, 0.12);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, #EF4444 28%, transparent);
}
.mkt-ql-pill.is-none { color: #A3A8BA; background: #F1F2F6; box-shadow: inset 0 0 0 1px #E3E5EC; }

/* Mentions: the number alone. A per-row bar repeated what the number already says and cost the
   row its air — the column is centred under its head instead. */
.mkt-ql-row .is-n { display: flex; align-items: center; justify-content: center; min-width: 0; }
.mkt-ql-n {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  font-weight: 600;
  color: var(--ink);
  flex: 0 0 auto;
  text-align: center;
}
.mkt-ql-row.is-gap .mkt-ql-n { color: var(--faint); }

/* The nine per-model column groups of the app's table, drawn as nine marks: lit = that model names
   the brand in its answer. Reading down the column shows which models know you. */
.mkt-ql-row .is-m { display: flex; align-items: center; justify-content: center; gap: var(--mgap); }
.mkt-ql-mark { display: block; flex: 0 0 auto; line-height: 0; }
.mkt-ql .mkt-ql-mark .mkt-llm { width: var(--mark); height: var(--mark); }
.mkt-ql-mark.is-off { opacity: 0.16; filter: grayscale(1); }

/* --- sizes ----------------------------------------------------------------------
 * The TABLE scales with the height it is given (the rows grow into it, so the type has to grow
 * with them); the TILES scale with width, on the same 620 / 470 / 430 buckets the sibling
 * visibility preview uses, so the two rows of boxes are the same size side by side. */

/* Tall previews (the single-column tablet preview, ~662x447 of content): the rows have the most
   height to share here, so the type steps up with them — a 13px line in a 70px row reads as a
   stranded row rather than an airy one. */
@container (min-height: 400px) {
  .mkt-ql { gap: 14px; }
  .mkt-ql-table { --row-h: 38px; --cols: minmax(0, 1fr) 36px 74px var(--marks); --mark: 16px; }
  .mkt-ql-steps { font-size: 13px; }
  .mkt-ql-head { font-size: 9px; padding-bottom: 8px; }
  .mkt-ql-row { font-size: 14.5px; }
  .mkt-ql-pill { min-width: 30px; height: 24px; font-size: 14px; border-radius: 8px; }
  .mkt-ql-n { font-size: 13.5px; }
}

/* Tall but NARROW (the ~490px container a 560px viewport gives): the rows have the tall branch's
   height but not its width, and at 14.5px in a 233px column the longest queries ran into the
   ellipsis. The type and the two metric columns step back to the short branch's sizes — the row
   heights, the gap and the air between the rows are the tall branch's and stay. */
@container (min-height: 400px) and (max-width: 540px) {
  .mkt-ql-table { --cols: minmax(0, 1fr) 30px 58px var(--marks); --mark: 13px; }
  .mkt-ql-head { font-size: 8.5px; }
  .mkt-ql-row { font-size: 12.5px; }
  .mkt-ql-pill { min-width: 24px; height: 20px; font-size: 12px; border-radius: 7px; }
  .mkt-ql-n { font-size: 12px; }
}

/* Short previews (~467x300 at 1024, ~320x279 on the phone): one notch tighter everywhere, so
   five rows still get their air. */
@container (max-height: 320px) {
  .mkt-ql { gap: 8px; }
  .mkt-ql-table { --row-h: 24px; }
  .mkt-ql-top { min-height: 16px; }
  .mkt-ql-steps { font-size: 11.5px; }
}

/* Wide previews: the tile size the sibling visibility preview uses at the same width. */
@container (min-width: 620px) {
  .mkt-ql-stats { gap: 12px; }
  .mkt-ql-stat { padding: 10px 13px 11px; border-radius: 12px; gap: 5px; }
  .mkt-ql-stat dt { font-size: 9.5px; }
  .mkt-ql-stat-val { font-size: 19px; }
  .mkt-ql-skel { height: 14px; }
}

/* Narrow previews (the 1024 column and the phone). The tile row falls back to THREE boxes here —
   the count the sibling visibility preview carries at every width. Four boxes in a 467px row leave
   each label 94px, and "Average position" needs 96: the contract is that a label is spelled out in
   full, so the box goes rather than the word. 9 + 3 already say what 12 says, and three boxes is
   the wider box the owner asked for. The table tightens but keeps all four columns. */
@container (max-width: 470px) {
  .mkt-ql-table { --cols: minmax(0, 1fr) 24px 46px var(--marks); --gap: 6px; --mark: 12px; --mgap: 2px; }
  .mkt-ql-stats { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
  .mkt-ql-stat.is-tracked { display: none; }
  .mkt-ql-stat { padding: 6px 8px 7px; }
  .mkt-ql-stat-val { font-size: 14px; }
  .mkt-ql-row { font-size: 11px; padding: 0 5px; }
  .mkt-ql-head { font-size: 7.5px; padding: 0 5px 4px; }
  .mkt-ql-pill { min-width: 20px; height: 16px; font-size: 10.5px; border-radius: 5px; }
  .mkt-ql-n { font-size: 10.5px; }
}

/* The phone column. The three tiles take the sibling preview's own phone box, down to the padding
   and the letter-spacing, so the two rows of boxes read the same at 390.
   The MODELS column goes here. Nine logos in a ~120px strip are a row of grey dots at this size,
   and they were costing the column the view is read for: the query had 152px against strings that
   need up to 210, so four of the five were cut mid-word ("Czy warto kupić Pegasus …" lost the model
   number). Without the strip the query has 228px and every row is spelled out. What is left is the
   app's own primary table — query, position, mentions — and the marks come back at 431px.
   The LLM chip goes too (the date chip says what the numbers were counted over, which is the one
   thing the frame cannot infer); the sibling leaderboard drops exactly the same chip at 390. */
@container (max-width: 430px) {
  .mkt-ql-stats { gap: 5px; }
  .mkt-ql-stat { padding: 5px 7px 6px; border-radius: 9px; gap: 2px; }
  .mkt-ql-stat dt { font-size: 8px; letter-spacing: 0.01em; }
  .mkt-ql-stat-val { font-size: 13px; }
  .mkt-ql-skel { height: 9px; }
  .mkt-ql-filters .mkt-ql-chip:first-child { display: none; }
  .mkt-ql-table { --cols: minmax(0, 1fr) 24px 46px; }
  .mkt-ql-head .is-m,
  .mkt-ql-row .is-m { display: none; }
}

/* --- sequence -------------------------------------------------------------------- */

.mkt-ql.is-armed .mkt-ql-chip { animation: ql-rise 0.3s ease calc(var(--i) * 0.08s) backwards; }

.mkt-ql.is-armed .mkt-ql-stats { animation: ql-fade 0.32s ease 0.06s backwards; }
.mkt-ql.is-armed .mkt-ql-stat { animation: ql-rise 0.34s ease calc(0.1s + var(--k) * 0.07s) backwards; }
.mkt-ql.is-armed .mkt-ql-skel {
  animation:
    ql-skel calc(1.33s + var(--k) * 0.08s) linear 0.22s both,
    ql-shimmer 0.67s linear 0.22s 2;
}
/* The tiles count once the rows are down — they are the summary of what just landed. The value
   appears a breath after its counter starts, so a tile is never caught showing a definite 0. */
.mkt-ql.is-armed .mkt-ql-stat-val { animation: ql-fade 0.3s ease calc(1.59s + var(--k) * 0.08s) backwards; }
.mkt-ql.is-armed .mkt-ql-stats .mkt-ql-num { animation: ql-count 0.62s ease-out calc(1.5s + var(--k) * 0.08s) both; }

.mkt-ql.is-armed .mkt-ql-head { animation: ql-fade 0.3s ease 0.1s backwards; }
.mkt-ql.is-armed .mkt-ql-rows::before { animation: ql-skeleton 1.4s ease 0.04s both; }
/* The rows land in order — the heart of the sequence — and each one's data arrives with it. */
.mkt-ql.is-armed .mkt-ql-row { animation: ql-row 0.36s cubic-bezier(0.22, 1, 0.36, 1) calc(0.4s + var(--i) * 0.13s) backwards; }
.mkt-ql.is-armed .mkt-ql-pill { animation: ql-pop 0.3s cubic-bezier(0.2, 0.9, 0.3, 1.3) var(--d) backwards; }
.mkt-ql.is-armed .mkt-ql-mark { animation: ql-pop 0.28s cubic-bezier(0.2, 0.9, 0.3, 1.3) calc(var(--d) + var(--m) * 0.022s) backwards; }
/* Same rule for the row's number: the count starts with the row, the digits become visible a
   breath later, so a landing row never flashes a "0" it does not mean. */
.mkt-ql.is-armed .mkt-ql-n { animation: ql-count 0.5s ease-out var(--d) both, ql-fade 0.2s ease calc(var(--d) + 0.1s) backwards; }

/* The status line narrates the run in two steps and then gets out of the way — spinner, words and
   all, by 1.82s, without ever spelling out a "gotowe" and without a green tick after it: a bare
   tick is the same success alert drawn as a glyph, and it has no sentence to stand beside once the
   narration ends. The sibling visibility preview narrates the same way. The two sentences hand the
   slot over on overlapping ramps (the outgoing one is rolling up and out while the incoming one
   rolls in), so the slot is never caught empty and the two are never both legible. Measured on a
   paused timeline: the faintest frame of the handover still carries about half the ink. Their resting
   state is invisible, so `both` fill lands back exactly where the no-JS frame is. */
.mkt-ql.is-armed .mkt-ql-spinner { animation: ql-spin 0.7s linear infinite, ql-hold 1.8s linear both; }
.mkt-ql.is-armed .mkt-ql-steps > .is-check { animation: ql-window 1.1s ease 0s both; }
.mkt-ql.is-armed .mkt-ql-steps > .is-rank { animation: ql-window 0.8s ease 1s both; }

/* Waiting for marketing.js to mark the widget live: hold the first frame. */
.mkt-widget:not(.is-live) .mkt-ql.is-armed *,
.mkt-widget:not(.is-live) .mkt-ql.is-armed *::after,
.mkt-widget:not(.is-live) .mkt-ql.is-armed *::before { animation-play-state: paused !important; }

@media (prefers-reduced-motion: reduce) {
  .mkt-ql *,
  .mkt-ql *::after,
  .mkt-ql *::before { animation: none !important; }
}

@keyframes ql-fade { from { opacity: 0; } }
@keyframes ql-rise { from { opacity: 0; translate: 0 4px; } }
@keyframes ql-pop { from { opacity: 0; scale: 0.6; } }
@keyframes ql-row { from { opacity: 0; translate: 0 6px; } }
@keyframes ql-window { 0% { opacity: 0; translate: 0 3px; } 12%, 88% { opacity: 1; translate: 0 0; } 100% { opacity: 0; translate: 0 -3px; } }
@keyframes ql-count { from { --ql-n: 0; } }
@keyframes ql-spin { to { rotate: 360deg; } }
@keyframes ql-hold { 0%, 96% { opacity: 1; } 100% { opacity: 0; } }
@keyframes ql-skel { 0% { opacity: 0; } 6%, 94% { opacity: 1; } 100% { opacity: 0; } }
@keyframes ql-skeleton { 0% { opacity: 0; } 10%, 62% { opacity: 1; } 100% { opacity: 0; } }
@keyframes ql-shimmer { from { background-position: 120% 0; } to { background-position: -120% 0; } }

/* === sentiment ============================================================= */
/*
 * Product page preview — Monitoring → "Sentyment" (widgets/v2/sentiment.blade.php).
 *
 * Every rule below describes the FINISHED view: the positive / neutral / negative donut with the
 * period's mention total, the per-model breakdown beside it and the strongest quotes underneath.
 * The sequence that leads there is a set of keyframes whose early frames hold the earlier states.
 * They apply only once the widget is armed by its inline script, sit paused until marketing.js
 * marks the widget live, and restart by themselves each time the accordion shows the widget
 * again (display: none cancels CSS animations).
 *
 * Timeline (s): the scaffold — the donut's grey track and its "0 wzmianki" centre, the mentions
 *               caption, the nine model rows with their logos, mention counts and empty tracks,
 *               and the mention cards with their loading placeholders — is painted on the very
 *               first frame and never
 *               animates, so the widget is never a blank panel; only the data moves, exactly as
 *               the audit-crawl benchmark opens · 0.45–1.4 the mentions are classified: the
 *               donut sweeps and the split counts up · 1.45–2.5 each model's bar fills segment
 *               by segment and its percentage badge lands · 2.4–3.0 the newest mentions arrive
 *               with their sentiment badges · 3.27 spinner → tick, "sentyment policzony" ·
 *               4.66–5.30 the narration clears itself.
 *               The four status lines tile the run with no gap, and not one of them belongs to
 *               the settled frame: what is left standing is the view — the filters it was read
 *               with, the donut, the split and the mentions — never a "done" banner. The line's
 *               base state is therefore "already over", which is exactly the frame the no-JS and
 *               reduced-motion readers get.
 *
 * Class prefix mkt-se-. The only shared class reused is .mkt-llm (model logo), and only ever
 * overridden under .mkt-se. Colours are the app's SENTIMENT_COLORS (lib/sentiment-helper.ts):
 * #22C55E positive, #F59E0B neutral, #EF4444 negative; the darker text tints match the badge
 * colours the app's MUI success/warning/error chips render.
 */

@property --se-n { syntax: '<integer>'; inherits: false; initial-value: 0; }

.mkt-se {
  /* A size container, not an inline-size one: the homepage SYSTEM stage is 16/9, i.e. as wide as
     the widest product preview and some ninety px shorter, so the buckets further down have to
     ask about height as well — keyed on width alone, that stage took the tall-frame layout and
     the model rows ran into the mentions under them. The body's height is set by its flex
     parent (.mkt-widget is height: 100%), so size containment has something definite to read. */
  container-type: size;
  --ink: #14162B;
  --muted: #656B7E;
  --line: #EDEEF2;
  --pos: #22C55E;
  --neu: #F59E0B;
  --neg: #EF4444;
  --pos-ink: #16A34A;
  --neu-ink: #D97706;
  --neg-ink: #DC2626;
}

/* The three bands live in a stage because a container query never matches the container element
   itself, and the gap between them is one of the things a short frame has to give up. */
.mkt-se-stage {
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1 1 auto;
  min-height: 0;
}

.mkt-se-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Counters: the integer sits in --to; the real number is in the visually hidden span, so a
   blocked stylesheet still reads correctly. */
.mkt-se-num { --se-n: var(--to); counter-reset: sen var(--se-n); }
.mkt-se-num::after { content: counter(sen); }

.mkt-se-cap {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* --- status line + filter chips ---------------------------------------------- */

.mkt-se-top { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; min-height: 18px; }
/* The narration, spinner and tick included. Its base state is "already over": no script, or
   reduced motion, and the row reads as the finished view does — the tick beside the reading the
   run ends on, with the filters that reading was measured over on the right. The run used to
   fade itself out here, which left the left half of the row empty on the settled frame
   (measured: 409x17 of nothing at 1440) while the chips kept the right. */
.mkt-se-run { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; min-width: 0; }
.mkt-se-state { position: relative; width: 14px; height: 14px; flex: 0 0 auto; }
.mkt-se-spinner {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid #E3E4FA;
  border-top-color: #5B5CE6;
  opacity: 0;
}
.mkt-se-tick { position: absolute; inset: 0; border-radius: 50%; background: var(--pos); }
.mkt-se-tick::after {
  content: "";
  position: absolute;
  left: 4.7px;
  top: 3px;
  width: 3.2px;
  height: 6.1px;
  border: solid #FFFFFF;
  border-width: 0 1.6px 1.6px 0;
  transform: rotate(45deg);
}
.mkt-se-steps { display: grid; flex: 1 1 auto; min-width: 0; font-size: 12.5px; font-weight: 500; color: #262A47; }
.mkt-se-steps > span {
  grid-area: 1 / 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  opacity: 0;
}
/* The reading the run ends on is the settled state — the one line that wraps instead of being
   clipped, because it is what a viewer without JS or with reduced motion reads. Same rule, same
   slot and same wording shape as the sibling position and leaderboard previews. */
.mkt-se-steps > .is-result {
  opacity: 1;
  white-space: normal;
  overflow: visible;
  text-wrap: pretty;
}
.mkt-se-filters { display: flex; gap: 6px; flex: 0 0 auto; }
.mkt-se-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border: 1px solid #E3E5EC;
  border-radius: 999px;
  background: #FFFFFF;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px;
  color: #4C5163;
  white-space: nowrap;
}
.mkt-se-chip::after {
  content: "";
  width: 4px;
  height: 4px;
  border: solid #9AA0B4;
  border-width: 0 1.2px 1.2px 0;
  transform: translateY(-1px) rotate(45deg);
}

/* --- the split + the per-model breakdown --------------------------------------- */

.mkt-se-main {
  display: grid;
  grid-template-columns: 158px minmax(0, 1fr);
  gap: 14px;
  flex: 1 1 auto;
  min-height: 0;
}

.mkt-se-share {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-width: 0;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: #FCFCFE;
}
.mkt-se-donut { position: relative; width: 96px; height: 96px; flex: 0 0 auto; }
.mkt-se-donut svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
.mkt-se-donut circle { fill: none; stroke-width: 5.5; }
.mkt-se-donut .is-track { stroke: #F1F2F6; }
.mkt-se-slice { stroke-dasharray: var(--len) 100; stroke-dashoffset: calc(var(--off) * -1); }
.mkt-se-donut-center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
}
.mkt-se-donut-center strong {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 19px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.mkt-se-donut-center em {
  font-style: normal;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.mkt-se-legend { display: flex; flex-direction: column; gap: 5px; margin: 0; width: 100%; }
.mkt-se-leg {
  display: grid;
  grid-template-columns: 7px minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 7px;
}
.mkt-se-leg::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--c);
}
.mkt-se-leg dt {
  min-width: 0;
  font-size: 11px;
  color: #4C5163;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mkt-se-leg dd {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin: 0;
  font-family: 'IBM Plex Mono', monospace;
  white-space: nowrap;
}
.mkt-se-leg-count { font-size: 10px; color: #9AA0B4; }
.mkt-se-leg-pct { font-size: 11px; font-weight: 600; color: var(--ink); }

.mkt-se-models { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
/* Nine models, one line each: model · mentions · bar · shares. The app draws a service row as
   two lines (name and badge over the bar), which for nine rows would have cost the frame the
   mentions strip under it. Every row is display: contents, so all nine share the four columns
   of this one grid: the names take the width of the longest ("AI Overviews"), the counts and the
   shares their own widest, and every bar starts and ends on the same x. The rows spread over the
   height the frame gives them; the gap is only the floor. */
.mkt-se-rows {
  display: grid;
  grid-template-columns: minmax(0, max-content) max-content minmax(0, 1fr) max-content;
  align-content: space-between;
  align-items: center;
  column-gap: 9px;
  row-gap: 5px;
  flex: 1 1 auto;
  min-height: 0;
}
.mkt-se-row { display: contents; }
.mkt-se-row-head { display: flex; align-items: center; gap: 6px; min-width: 0; }
.mkt-se .mkt-se-row-head .mkt-llm { width: 14px; height: 14px; }
.mkt-se-model {
  min-width: 0;
  font-size: 11.5px;
  color: #262A47;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The model's own mention count, so each row's bar is readable as a quantity and the nine add
   up to the donut's centre. */
.mkt-se-mentions {
  justify-self: end;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px;
  color: #9AA0B4;
}
/* The app prints these three shares as one bordered badge next to the model's name. Here the bar
   beside them already draws the same split, so the numbers are left as plain mono text: nine
   pills stacked down the frame were the one piece of chrome this view could lose. */
.mkt-se-mix {
  justify-self: end;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: 'IBM Plex Mono', monospace;
  white-space: nowrap;
}
.mkt-se-mix i { font-style: normal; font-size: 10px; color: #B4B8C7; }
.mkt-se-mix b { font-size: 10.5px; font-weight: 600; }
.mkt-se-mix .is-pos { color: var(--pos-ink); }
.mkt-se-mix .is-neu { color: var(--neu-ink); }
.mkt-se-mix .is-neg { color: var(--neg-ink); }

.mkt-se-stack {
  display: flex;
  height: 6px;
  border-radius: 999px;
  overflow: hidden;
  background: var(--line);
}
.mkt-se-stack i { width: calc(var(--w) * 1%); height: 100%; transform-origin: left center; }
.mkt-se-stack .is-pos { background: var(--pos); }
.mkt-se-stack .is-neu { background: var(--neu); }
.mkt-se-stack .is-neg { background: var(--neg); }

/* --- the quotes ----------------------------------------------------------------- */

.mkt-se-quotes { display: flex; flex-direction: column; gap: 6px; flex: 0 0 auto; }
.mkt-se-quote-row { display: flex; gap: 8px; }
.mkt-se-quote {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin: 0;
  padding: 7px 9px 8px;
  border: 1px solid var(--line);
  border-left: 2px solid var(--c, var(--line));
  border-radius: 4px 10px 10px 4px;
  background: #FCFCFE;
}
.mkt-se-quote.is-pos { --c: var(--pos); }
.mkt-se-quote.is-neu { --c: var(--neu); }
.mkt-se-quote.is-neg { --c: var(--neg); }
/* Until the mention lands the card holds a placeholder, not an empty frame: a bar where the
   model's name goes, a pill where its sentiment badge goes and three lines where the mention
   itself goes — the same answer the content-demo card gives its description slot and the report
   widget gives its rows, so a waiting card reads the same in every widget. inset + padding:
   inherit puts it on the card's own content box, so each bucket's padding carries over. Its
   resting opacity is 0: the settled frame — which is also the no-JS and reduced-motion frame —
   never shows it. The card's coloured left edge waits with it (see the sequence), so the frame
   does not announce "one positive, one negative" before a single mention has been read. */
.mkt-se-skel {
  position: absolute;
  inset: 0;
  padding: inherit;
  display: flex;
  flex-direction: column;
  gap: 5px;
  pointer-events: none;
  opacity: 0;
}
.mkt-se-skel b { display: flex; align-items: center; gap: 6px; }
/* The same grey the empty bar tracks above are drawn in, so the waiting frame is one value. */
.mkt-se-skel i { display: block; flex: 0 0 auto; height: 8px; border-radius: 4px; background: var(--line); }
.mkt-se-skel b i:first-child { width: 44%; height: 9px; }
.mkt-se-skel b i:last-child { margin-left: auto; width: 42px; height: 14px; border-radius: 999px; }
.mkt-se-skel > i:nth-of-type(1) { width: 100%; margin-top: 2px; }
.mkt-se-skel > i:nth-of-type(2) { width: 92%; }
.mkt-se-skel > i:nth-of-type(3) { width: 62%; }
/* Two mentions, with room around them, are the default: the third card only comes back where the
   frame is both wide and tall. The pair that always stays is the newest and the negative one —
   the two that explain the donut's green and red and Grok's quarter-red bar. */
.mkt-se-quote:nth-child(3) { display: none; }
.mkt-se-quote-head { display: flex; align-items: center; gap: 5px; min-width: 0; }
.mkt-se .mkt-se-quote-head .mkt-llm { width: 12px; height: 12px; }
.mkt-se-quote-model {
  min-width: 0;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mkt-se-badge {
  margin-left: auto;
  padding: 0 6px;
  border: 1px solid;
  border-radius: 999px;
  font-size: 9px;
  font-weight: 500;
  line-height: 15px;
  white-space: nowrap;
}
.mkt-se-badge.is-pos { color: var(--pos-ink); border-color: #A7E9BF; background: #F0FDF4; }
.mkt-se-badge.is-neu { color: var(--neu-ink); border-color: #F6D28C; background: #FFFBEB; }
.mkt-se-badge.is-neg { color: var(--neg-ink); border-color: #F5AFAF; background: #FEF2F2; }
.mkt-se-quote blockquote {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin: 0;
  font-size: 10.5px;
  line-height: 1.45;
  color: #4C5163;
  text-wrap: pretty;
}

/* The buckets. The body is a size container, so each one is keyed on the CONTENT box the frame
   leaves the widget — its own box minus the body's 40/38px padding. Measured, not guessed (the
   page is zoomed .9 above a 1000px viewport, and container queries resolve in unzoomed px):
     308×269  /produkt at 390, the square frame   → narrow
     308×310  the SYSTEM tab at 390               → narrow
     467×300  /produkt at 1024                    → short
     518×311  the SYSTEM tab at 860               → short
     544×327  the SYSTEM tab at 1024              → short
     558×368  /produkt at 1440 and 1920           → base
     656×353  the SYSTEM tab at 1280 and wider    → base
     662×401  the SYSTEM tab at 768               → base
     662×447  /produkt at 768                     → wide
   Width alone would drop the 16/9 SYSTEM stage into the wide bucket, whose height it does not
   have: that is what used to run the model rows into the mentions below them. */

/* Wide AND tall (the 768 preview): room for a third mention, a bigger donut and the larger type
   the app itself uses. */
@container (min-width: 620px) and (min-height: 420px) {
  .mkt-se-stage { gap: 12px; }
  .mkt-se-main { grid-template-columns: 186px minmax(0, 1fr); gap: 18px; }
  .mkt-se-steps { font-size: 13px; }
  .mkt-se-donut { width: 118px; height: 118px; }
  .mkt-se-donut-center strong { font-size: 22px; }
  .mkt-se-donut-center em { font-size: 8.5px; }
  .mkt-se-legend { gap: 7px; }
  .mkt-se-leg dt { font-size: 12px; }
  .mkt-se-leg-count { font-size: 11px; }
  .mkt-se-leg-pct { font-size: 12px; }
  .mkt-se-model { font-size: 12.5px; }
  .mkt-se-mentions { font-size: 10.5px; }
  .mkt-se-mix b { font-size: 11px; }
  .mkt-se-stack { height: 7px; }
  /* The one frame with room for the third mention. Three cards share it, so each is narrow: four
     lines is what the longest one needs to be read whole rather than cut off mid-sentence. */
  .mkt-se-quote:nth-child(3) { display: flex; }
  .mkt-se-quote blockquote { font-size: 11.5px; -webkit-line-clamp: 4; }
  .mkt-se-quote-model { font-size: 10px; }
}

/* Narrow previews (the phone column, where the frame is square): the donut and its legend sit
   side by side as a strip, the nine model rows keep their bars but drop the raw counts, and the
   mentions give the frame up (see the end of this bucket). */
@container (max-width: 430px) {
  .mkt-se-stage { gap: 8px; }
  /* The narration row goes entirely, chips and reading with it. On a 307px frame the row is 26px
     under the chrome bar that the donut, the nine model rows and their bars all need more than a
     sentence does — the same trade the mentions strip below already makes at this width. The
     sequence still plays here (the donut sweeps, the bars fill, the counts tick); only the words
     go. The 26px go to the view. */
  .mkt-se-top { display: none; }
  .mkt-se-main { grid-template-columns: minmax(0, 1fr); gap: 8px; }
  .mkt-se-share {
    flex-direction: row;
    align-items: center;
    gap: 12px;
    padding: 6px 9px;
  }
  /* Small enough for the square frame; the label is set below the ring's inner chord at this
     size, so it loses a little tracking rather than touching the ring. Measured, not guessed:
     at 62px the (then two-line) rows below pushed 10px into the body's 14px bottom padding and
     the last bar sat ~4px off the frame edge — the donut, this padding and the row rhythm
     together buy that space back. */
  .mkt-se-donut { width: 57px; height: 57px; }
  /* A thinner ring at this size, for the hole rather than for the look: at 5.5 the unit under the
     total ran onto the stroke at both ends (measured at 57px the inner chord under the number is
     ~31px and the tracked label ~31px wide). 4.6 buys ~2px of hole on each side, and the label
     gives its tracking back — the two together clear the ring. */
  .mkt-se-donut circle { stroke-width: 4.6; }
  .mkt-se-donut-center strong { font-size: 13px; }
  .mkt-se-donut-center em { font-size: 5.8px; letter-spacing: 0.02em; }
  .mkt-se-legend { gap: 4px; }
  .mkt-se-leg dt { font-size: 10px; }
  .mkt-se-leg-count { display: none; }
  .mkt-se-leg-pct { font-size: 10.5px; }
  /* Nine rows have to fit the square frame: at this size the row rhythm is what gives, not the
     number of models — the per-model split is the point of the view. The count column goes, so
     the grid drops to three columns (a hidden cell would still leave its gap behind). */
  .mkt-se-rows { grid-template-columns: minmax(0, max-content) minmax(0, 1fr) max-content; column-gap: 8px; row-gap: 3px; }
  .mkt-se-model { font-size: 10.5px; }
  .mkt-se-mentions { display: none; }
  .mkt-se-mix { gap: 3px; }
  .mkt-se-mix b { font-size: 9.5px; }
  .mkt-se-mix i { font-size: 9px; }
  .mkt-se-stack { height: 5px; }
  .mkt-se .mkt-se-row-head .mkt-llm { width: 12px; height: 12px; }
  /* The square phone frame fits the donut strip and all nine model rows, but not a readable
     mention on top of them — two clamped lines would say nothing. The split keeps the frame. */
  .mkt-se-quotes { display: none; }
}
/* Short frames: the 1024 preview (467×300) and every SYSTEM stage between 518 and 656px wide,
   none of them taller than 327px. The same layout as the base rules, with a smaller donut and a
   tighter row pitch — which is what keeps the nine model rows off the mentions under them. */
@container (min-width: 431px) and (max-height: 340px) {
  .mkt-se-stage { gap: 8px; }
  /* Wider than the base column, not narrower: this frame renders under the page's 0.9 zoom, and
     at 140/158px "Pozytywne" lost its tail to an ellipsis next to the count and the share. */
  .mkt-se-main { grid-template-columns: 175px minmax(0, 1fr); gap: 12px; }
  .mkt-se-share { gap: 8px; padding: 7px 8px; }
  /* The donut had lost a fifth of its diameter against the base bucket while the unit under the
     total kept its size and its tracking: measured at 1024, "WZMIANKI" was 36.0px wide inside a
     23.2px hole — 1.5px of air, against 5.6px at 1440 and 4.4px on the 768 SYSTEM stage, the
     tightest frame in the family. The hole is what was missing, not the label: the share card
     here carries the height of the model rows beside it and was leaving 15px of it unused
     under the legend, so the ring takes that room back and the label keeps the size and the
     0.08em it has in every other frame. Measured after: 5.8px of air at 1024, 6.5px at 860. */
  .mkt-se-donut { width: 92px; height: 92px; }
  .mkt-se-donut-center strong { font-size: 16px; }
  .mkt-se-donut-center em { font-size: 7px; }
  .mkt-se-legend { gap: 4px; }
  .mkt-se-leg dt { font-size: 10px; }
  .mkt-se-leg-count { font-size: 9.5px; }
  .mkt-se-leg-pct { font-size: 10.5px; }
  .mkt-se-rows { column-gap: 8px; row-gap: 3px; }
  .mkt-se-model { font-size: 11px; }
  .mkt-se-mentions { font-size: 9.5px; }
  .mkt-se-mix { gap: 3px; }
  .mkt-se-mix b { font-size: 10px; }
  .mkt-se-mix i { font-size: 9px; }
  .mkt-se-stack { height: 5px; }
  .mkt-se-quotes { gap: 5px; }
  .mkt-se-quote { gap: 4px; padding: 6px 8px 7px; }
  .mkt-se-quote-model { font-size: 9px; }
  .mkt-se-badge { font-size: 8.5px; line-height: 14px; }
  /* Three lines, not two: at this card width both mentions need a third line to finish their
     sentence, and the frame has the room for it. */
  .mkt-se-quote blockquote { font-size: 10.5px; -webkit-line-clamp: 3; }
}

/* --- sequence ------------------------------------------------------------------- */

/* Nothing structural animates. The donut track, the "0 wzmianki" centre, the legend rows with
   their coloured dots and zeros, the mentions caption, the nine model rows (logo, name, mention
   count, empty track) and the mention cards (frame, neutral left edge, placeholder lines) are all
   painted on the first frame — every slot in the view carries a placeholder, so the widget opens
   as a view waiting for data and never as a white panel. Only the data itself moves. */

.mkt-se.is-armed .mkt-se-chip { animation: se-rise 0.3s ease calc(0.08s + var(--i) * 0.08s) backwards; }

/* Classifying: the donut sweeps slice by slice and the centre and legend count up. */
.mkt-se.is-armed .mkt-se-slice { animation: se-slice 0.5s cubic-bezier(0.22, 1, 0.36, 1) calc(0.45s + var(--k) * 0.12s) backwards; }
.mkt-se.is-armed .mkt-se-donut-center strong { animation: se-count 1s ease-out 0.45s both; }
.mkt-se.is-armed .mkt-se-leg .mkt-se-num { animation: se-count 0.55s ease-out calc(0.95s + var(--k) * 0.12s) both; }

/* Splitting: which models answer is known before what they think of the brand is, so each row's
   colours and its percentage badge land only once that model's mentions have been classified.
   Nine rows share the 1.45–2.5s window six used to: 0.065s a row ends the last bar at 2.5s. */
.mkt-se.is-armed .mkt-se-stack i { animation: se-bar 0.42s cubic-bezier(0.22, 1, 0.36, 1) calc(1.45s + var(--k) * 0.065s + var(--s) * 0.055s) backwards; }
.mkt-se.is-armed .mkt-se-mix { animation: se-pop 0.3s cubic-bezier(0.2, 0.9, 0.3, 1.3) calc(1.47s + var(--k) * 0.065s) backwards; }
.mkt-se.is-armed .mkt-se-mix .mkt-se-num { animation: se-count 0.5s ease-out calc(1.47s + var(--k) * 0.065s) both; }

/* The mentions. The cards' frames are already there, each holding its placeholder and a neutral
   left edge; the placeholder hands over to the mention exactly as it rises, and the edge takes
   its sentiment colour with it — the verdict arrives with the quote, not two and a half seconds
   before it. */
.mkt-se.is-armed .mkt-se-skel { animation: se-skel calc(2.64s + var(--q) * 0.12s) linear both; }
.mkt-se.is-armed .mkt-se-quote { animation: se-edge 0.34s ease calc(2.5s + var(--q) * 0.12s) backwards; }
.mkt-se.is-armed .mkt-se-quote-head { animation: se-rise 0.32s ease calc(2.5s + var(--q) * 0.12s) backwards; }
.mkt-se.is-armed .mkt-se-quote blockquote { animation: se-rise 0.34s ease calc(2.56s + var(--q) * 0.12s) backwards; }
.mkt-se.is-armed .mkt-se-badge { animation: se-pop 0.3s cubic-bezier(0.2, 0.9, 0.3, 1.3) calc(2.7s + var(--q) * 0.12s) backwards; }

/* Four status lines tiling 0 → 3.27s with no gap: the first one is already on screen at frame
   zero (a window would fade it in from nothing), each hands over to the next, and the mentions
   beat is narrated like the other two. Each line's fade-out runs past the next line's fade-in
   (measured: no instant where every line sits below 5% opacity), the way the benchmark's
   is-scan still reads while is-result rises. The tick lands WITH the last line, and that last
   line is not a "done" banner but the reading of the frame underneath it — so the run does not
   have to clear itself to avoid leaving a stale alert on the settled view, and the row is never
   half empty. Nothing below the row moves at any point: the three narration lines and the
   reading share one grid cell. */
.mkt-se.is-armed .mkt-se-spinner { animation: se-spin 0.7s linear infinite, se-hold 3.27s linear both; }
.mkt-se.is-armed .mkt-se-tick { animation: se-land 0.34s cubic-bezier(0.2, 0.9, 0.3, 1.3) 3.27s backwards; }
.mkt-se.is-armed .mkt-se-steps > .is-read { animation: se-first 1.55s ease both; }
.mkt-se.is-armed .mkt-se-steps > .is-split { animation: se-window 1.05s ease 1.4s both; }
.mkt-se.is-armed .mkt-se-steps > .is-quotes { animation: se-window 1s ease 2.4s both; }
.mkt-se.is-armed .mkt-se-steps > .is-result { animation: se-rise-in 0.3s ease 3.27s backwards; }

/* Waiting for marketing.js to mark the widget live: hold the first frame. */
.mkt-widget:not(.is-live) .mkt-se.is-armed *,
.mkt-widget:not(.is-live) .mkt-se.is-armed *::after,
.mkt-widget:not(.is-live) .mkt-se.is-armed *::before { animation-play-state: paused !important; }

@media (prefers-reduced-motion: reduce) {
  .mkt-se *,
  .mkt-se *::after,
  .mkt-se *::before { animation: none !important; }
}

@keyframes se-rise { from { opacity: 0; translate: 0 4px; } }
@keyframes se-pop { from { opacity: 0; scale: 0.7; } }
/* The last beat lands on the tick and on the reading, whose own resting state IS visible: both
   keyframes therefore only need to hold the hidden first frame (`backwards`) and then run into
   the base state. The `to` is spelled out anyway, the way the benchmark's crawl-rise does, so the
   pair still reads correctly if either base opacity is ever changed back. */
@keyframes se-rise-in { from { opacity: 0; translate: 0 4px; } to { opacity: 1; translate: 0 0; } }
@keyframes se-land { from { opacity: 0; scale: 0.7; } to { opacity: 1; scale: 1; } }
@keyframes se-window { 0% { opacity: 0; translate: 0 3px; } 12%, 88% { opacity: 1; translate: 0 0; } 100% { opacity: 0; translate: 0 -3px; } }
/* Like se-window, but already legible at 0%: the opening status line must be on the first frame.
   The explicit opacity: 1 start matters — the element's own base opacity is 0. */
@keyframes se-first { 0%, 84% { opacity: 1; translate: 0 0; } 100% { opacity: 0; translate: 0 -3px; } }
/* The placeholder's own life: on from the first frame, and handed over rather than switched off
   — its fade runs 2.46-2.64s, across the instant the mention begins to rise, so the card
   crossfades instead of dimming to nothing first. The element's resting opacity is 0, so `both` leaves
   nothing behind on the settled frame. */
@keyframes se-skel { 0%, 93% { opacity: 1; } 100% { opacity: 0; } }
@keyframes se-edge { from { border-left-color: #E9EBF2; } }
@keyframes se-slice { from { stroke-dasharray: 0 100; } }
@keyframes se-bar { from { transform: scaleX(0); } }
@keyframes se-count { from { --se-n: 0; } }
@keyframes se-spin { to { rotate: 360deg; } }
@keyframes se-hold { 0%, 96% { opacity: 1; } 100% { opacity: 0; } }

/* === strategy-card ========================================================= */
/*
 * Product page preview — /produkt-claude-preview, step 3 "Działaj" → Strategie.
 * Widget: partials/marketing/widgets/v2/strategy-card.blade.php
 *
 * Every rule below describes the SAVED strategy list: the card with its name, intent, the three
 * related AI queries with their own score and position, and the metrics derived from exactly those
 * queries — then the second strategy, which has no queries pinned and says so in a line rather
 * than in two empty slots, and the row that starts a new one. Nothing else: no counters over the
 * two cards or the three rows, no count of the monitored queries the strategy was cut from,
 * no "done" banner and no status row. The act of building the first card is a set of keyframe
 * animations whose "from" frames hold the earlier states: the same card, still open as the "Nowa
 * strategia" form with its query picker. They apply only once the widget is armed by its inline
 * script, sit paused until marketing.js marks the widget live, and restart by themselves each time
 * the accordion shows the widget again (display: none cancels CSS animations).
 *
 * Timeline (s): 0.05 the form opens · 0.20–0.85 the name is typed · 0.84–1.29 the intent is
 * written · 0.45–1.29 the field's helper line holds the room the picked values will take · 1.26
 * the query picker opens under an EMPTY field, into exactly that room, three of the project's
 * monitored queries in view · 1.48 the first is ticked and lands in the field as a value
 * (1.51–1.77), making it a row taller and squeezing the open listbox to two rows · 2.10 / 2.62
 * the other two are ticked, in the two rows that squeeze leaves standing · 3.02–3.28 both land
 * together and the picker closes over them, its top edge running down to the window's floor as
 * their rows grow · 3.42 "Zapisz" is pressed · 3.60
 * the form settles into a saved card, its rule is drawn and the values become its rows · 3.66 the
 * list's label takes the cell the field's text line vacates · 3.78–4.50 the three per-query
 * scores tick, the ring closes on their average and the average position lands · 4.53 the frame is settled and nothing moves again. Nothing above
 * the card ever opens or closes, so the list starts and ends in the same place.
 *
 * There are no empty slots anywhere in that run, and never were any picked queries before they
 * were picked: a row does not exist until its value lands, and arrives from no height and no
 * seam. The three things that grow with it — the field's outline, the popper hanging off it and
 * the row itself — run on one 3.3s clock so they can never drift apart.
 *
 * Two rules keep the middle of the sequence clean. Nothing that is being replaced and its
 * replacement are ever legible at the same time in the same cell: the popper is always retreating
 * out of the slot the value is growing into, on the same curve, so the row is uncovered rather
 * than drawn over — and it retreats by exactly what the row takes, so the room under the field
 * is never uncovered either. And nothing leaves before it has been acted on: "Zapisz" is pressed
 * at full opacity and only then does the form dissolve.
 *
 * ONE box in the sequence changes, once: the card's own. While the strategy is only being named
 * and described, the window that holds the picked queries is exactly as tall as what is in it —
 * one helper line — so the card is the height an empty form needs. It used to reserve all three
 * rows from the first frame, which is the whole reason the opening frame read as a form waiting
 * to be filled, and the opening frame is still the short one.
 *
 * At 1.10-1.30, as the picker unfurls, the room those three rows will take moves from the foot of
 * the list into the window as padding (sc-grow hands it over, sc-room takes it). From there to
 * the end the window's box is exactly --winh whatever is in it: the padding gives back precisely
 * what each arriving row takes, on the same clock and the same curve, so nothing in the card or
 * under it moves again. That one fixed number is what the footer stands on and what the picker is
 * measured against — the picker's lower edge is the window's own edge plus --popdrop and nothing
 * else, which is what keeps it off "Anuluj / Zapisz" and off "Dodaj strategię" at every width.
 *
 * Everything else in the card holds its box throughout: the field's outline and the picker are
 * overlays (::before and an absolutely placed popper), and the field line and the scope line
 * share one grid cell. The card is flex: 1 1 auto inside the square, so from 1.30 on it is simply
 * what the square has left over — the frame the widget rests on is the frame it works in, and the
 * blank under the row that starts the next strategy closes at 1.30 instead of at 3.30.
 *
 * The preview is a square from 350px (phone) to ~700px (tablet column), so every size is a clamp
 * on cqw — the card keeps its proportions instead of being squeezed by the flexbox when the square
 * is small. `sc` is the widget body, `sccard` the strategy being built. The only shared classes
 * reused are .mkt-pill (position) and .mkt-wiz-add-plus, both only ever overridden under .mkt-sc.
 */

@property --sc-n { syntax: '<integer>'; inherits: false; initial-value: 0; }

.mkt-sc {
  /* The frame's own vertical room — the padding above the first card and below the add row, and
     the seams between the list's blocks — is set below, by the square, not here. Both were
     viewport-driven (.mkt-widget-body's `padding: 18px 20px 20px`, dropped to 14px by a
     `max-width: 640px` media query, and a `1.5vw` seam), and at a viewport of 961-999 the preview
     is a ~420px square with a ~1000px viewport's spacing around it: 18 + 20 of padding and two
     11px seams where the same square at a 460px viewport gets 14 + 14 and two 6.9px seams. Those
     18px came out of the only flexible box in the frame — the card — and pushed the action row
     out through its bottom edge. Zeroed here and re-declared on boxes that are INSIDE the
     container, which is the only place a cqw can be read: an element is never matched against
     its own container, so neither `padding` nor `gap` on .mkt-sc can be measured in cqw. */
  gap: 0;
  padding-block: 0;
  justify-content: flex-start;
  container: sc / inline-size;

  --ink: #14162B;
  --muted: #656B7E;
  --faint: #8A8FA3;
  --line: #EDEEF2;
  --brand: #5B5CE6;
  --brand-dark: #4B4CD8;
  --brand-soft: #EEF0FE;
  --brand-line: #C7C8F6;
  --ok: #22C55E;

  /* One measure for the query rows, the picker options and the picker's scroll step. */
  --row: clamp(24px, 5.5cqw, 32px);
  --rgap: clamp(5px, 1.15cqw, 8px);
  --rpad: clamp(6px, 1.6cqw, 11px);
  --cgap: clamp(5px, 1.4cqw, 9px);
  --wscore: clamp(17px, 4.1cqw, 26px);
  --wpill: clamp(17px, 4.2cqw, 24px);
}

/* The frame's vertical room, measured against the square the widget is standing in. Two spacer
   boxes for the padding — a pseudo-element's container query unit resolves against its
   originating element's container, so ::before and ::after of .mkt-sc DO read `sc`, where the
   element's own padding cannot — and a margin for the seam, on the blocks themselves. Every
   number is today's at the sizes that were right: 12/13 and a 6px seam at a 350px preview (the
   390 phone), 18/20 and 11px from a ~470px preview up (1024, 1440, the 768 column). What changes
   is the ~420px preview the two-column grid hands this widget between 961 and 999, which now
   gets a 420px preview's spacing instead of a 1000px viewport's: 23px back to the card. */
.mkt-sc::before,
.mkt-sc::after { content: ""; display: block; flex: 0 0 auto; }
.mkt-sc::before { height: clamp(12px, calc(6.7cqw - 13.5px), 18px); }
.mkt-sc::after { height: clamp(13px, calc(8.75cqw - 21.1px), 20px); }
.mkt-sc > * + * { margin-top: clamp(6px, calc(5.75cqw - 16px), 11px); }

.mkt-sc-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.mkt-sc-num { --sc-n: var(--to); counter-reset: sc var(--sc-n); }
.mkt-sc-num::after { content: counter(sc); }

/* --- no status line ------------------------------------------------------------ */

/* There is none, on purpose. A narration row over the card would say a second time what the
   card is already showing — the name being typed, the picker, the ticks, "Zapisz", the metrics
   derived — and it could not stay: a row held for a line that is gone is an empty band, so it
   had to close at the end and lifted the whole saved list by its own height plus the seam, 27px
   at 1440, exactly as the frame settled. The card carries the dynamics on its own; the first
   frame and the settled frame now start at the same place and nothing above the card ever takes
   or gives back room. */

/* --- the strategy being built --------------------------------------------------- */

.mkt-sc-card { border: 1px solid #DEE0E8; border-radius: clamp(10px, 2.4cqw, 16px); background: #FFFFFF; }

/* The row pitch, and everything measured in it. Derived HERE and not on .mkt-sc, because the
   container queries below retune --row and --rgap on these elements: a var() is substituted
   against the element it is declared on, so a --step left up on .mkt-sc would keep the widest
   preview's row pitch at every other size and the listbox would scroll by the wrong amount.
   --winh is the window the three picked queries end at, a destination now rather than a
   reservation: it sizes the popper, and the room the card's growth needs.
   On BOTH the card and the row that closes the list, because that row is where the room is held
   (sc-grow) and the two have to count in the same rows to the pixel. They resolve identically:
   `sccard` is the card's own container, so the card's own properties — like the row's margin —
   are measured against `sc` just as the add row's are. --hint-f is the helper line under the
   empty field, the window's whole content until the first query lands and therefore a measure of
   the card's height too; declared as the font-size it is rendered at, times its line-height, so
   the two can never disagree. */
.mkt-sc-card.is-new,
.mkt-sc-add {
  --step: calc(var(--row) + var(--rgap));
  --winh: calc(3 * var(--row) + 2 * var(--rgap));
  --hint-f: clamp(9px, 2cqw, 11.5px);
  --hint-h: calc(var(--hint-f) * 1.45);
  /* The only thing the picker is allowed to add to the field's own room: the 2px it hangs below
     the field's line plus the 6px of listbox border and padding. The picker is sized by the
     window's box and this, and by nothing else — which is what keeps it off the row of buttons
     underneath it at every width. */
  --popdrop: 8px;
}

.mkt-sc-card.is-new {
  container: sccard / inline-size;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  /* A FLOOR, not a measure. The card is flex: 1 1 auto in a square and space-between owns what is
     left over, so the three seams always add up to (card - sections) however small this is: while
     the card has a pixel of room to spare the value is invisible, and the moment it has none it is
     the difference between a seam and an action row hanging out of the bottom edge. It used to be
     tuned as if it were the seam itself, which is why the card's content could be taller than the
     card at the two sizes where the square gives it nothing spare (a ~420px preview, and the
     ~605px one where the roomy set starts). */
  gap: clamp(4px, 1.1cqw, 12px);
  flex: 1 1 auto;
  min-height: 0;
  padding: clamp(8px, 2.2cqw, 16px) clamp(9px, 2.4cqw, 17px);
  /* Not hidden: while the card is still short the open picker reaches a dozen-odd pixels past its
     bottom edge, into the room being held for the growth — which is what a dropdown does to the
     thing it drops out of, and the only place it has to go. (Clipping it instead sliced a third off
     the listbox's last row at 390, mid-glyph.) Nothing else reaches an edge: the card is
     flex: 1 1 auto and its content was measured inside it at every stop of the growth from a 348px
     preview up. Below a ~300px preview — a viewport under ~340, narrower than this page is built
     for — the settled card's own content is taller than the square can give it, as it was before
     any of this; what used to be clipped there now shows. */
  overflow: visible;
  box-shadow: 0 14px 30px -24px rgba(20, 22, 43, 0.55);
}
/* The sections keep their own height; the square's spare room goes between them. */
.mkt-sc-card.is-new > * { flex: 0 0 auto; }

/* Name — a text field while the strategy is written, a heading once it is saved. */
.mkt-sc-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 10px; }
.mkt-sc-field { flex: 0 1 auto; min-width: 0; padding-bottom: clamp(3px, 0.9cqw, 6px); border-bottom: 1.5px solid transparent; }
.mkt-sc-name {
  position: relative;
  margin: 0;
  width: max-content;
  max-width: 100%;
  font-size: clamp(11px, 3.3cqw, 16px);
  line-height: 1.25;
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--ink);
}
/* The typed reveal interpolates from inset(0 100% 0 0) — it needs a real base to animate from. */
.mkt-sc-name-text { display: block; white-space: nowrap; clip-path: inset(0 0 0 0); }
.mkt-sc-caret {
  position: absolute;
  left: 100%;
  top: 50%;
  translate: 0 -50%;
  width: 1.6px;
  height: 1.05em;
  border-radius: 1px;
  background: var(--brand);
  opacity: 0;
}
.mkt-sc-tools { display: flex; gap: 4px; flex: 0 0 auto; }
.mkt-sc-tool {
  width: clamp(19px, 4.4cqw, 26px);
  height: clamp(19px, 4.4cqw, 26px);
  border-radius: 7px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--faint);
  background: #F6F7FA;
}
.mkt-sc-tool svg { width: 55%; height: 55%; }

/* Intent — the textarea tint is a box-shadow spread, so losing it moves nothing. */
.mkt-sc-intent {
  margin: 0;
  border-radius: 4px;
  font-size: clamp(10.3px, 2.45cqw, 13px);
  line-height: 1.5;
  color: #4C5163;
  background: transparent;
  box-shadow: 0 0 0 7px transparent;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.mkt-sc-chars > i { font-style: normal; }

/* --- the queries the strategy is built on --------------------------------------- */

/* --pgap is the seam inside this block AND the one the field's outline has to clear when it is
   still empty, so it is a property, not a literal gap: the container queries below retune it in
   one place and the outline follows. */
.mkt-sc-pin { position: relative; z-index: 2; display: flex; flex-direction: column; --pgap: clamp(4px, 1.2cqw, 8px); gap: var(--pgap); }
.mkt-sc .mkt-widget-kicker { margin: 0; font-size: clamp(8.5px, 1.95cqw, 10.5px); }
/* No header row over the rows any more, and no header row's height either. It used to carry the
   list's label on the left and the word "wynik" over the score column on the right; the column
   header is gone (one of two numbers labelled is worse than neither), and the label moved into
   the field's own line below, where the count of monitored queries used to sit. That row and its
   seam — 21px + --pgap at every width, because the row's height was the flex strut and not the
   10.5px label inside it — came out of the card in BOTH states, the form and the saved card, so
   nothing in the sequence is measured differently; the card simply has ~27px more to spend on the
   seams that were tightest, which is where the action row stands. */

/* THE FIELD. One box holds the queries picked so far — the app's `Autocomplete multiple` — so it
   starts with nothing in it and grows by a row per pick. Its outline is an overlay (::before), not
   a border: the box can grow without moving a single line of the card, and the base state, which
   is the saved card and therefore also the no-JS and reduced-motion frame, simply has no field. */
.mkt-sc-box { --fx: clamp(4px, 1.4cqw, 9px); position: relative; display: flex; flex-direction: column; gap: var(--pgap); min-width: 0; }
/* Named, not `> *`: the popper is a child too and must keep its own position and stacking. */
.mkt-sc-box > .mkt-sc-line,
.mkt-sc-box > .mkt-sc-win { position: relative; z-index: 1; }
.mkt-sc-box::before {
  content: "";
  position: absolute;
  z-index: 0;
  /* Bottom is what grows: from just under the text line (the field is empty) down to the last
     picked row. 2px of air on every side, so the outline sits outside the rows, not on them. */
  inset: -2px calc(-1 * var(--fx)) -2px;
  border: 1px solid var(--brand-line);
  border-radius: clamp(7px, 1.8cqw, 11px);
  background: #FFFFFF;
  box-shadow: 0 0 0 2.5px rgba(91, 92, 230, 0.12);
  opacity: 0;
}

/* The field's text line and the saved card's label share one cell. */
.mkt-sc-line { display: grid; min-width: 0; }
.mkt-sc-line > * { grid-area: 1 / 1; align-self: center; min-width: 0; }
/* The block padding is what gives the field its text line its height — it stays in the base state
   (where the element is invisible) so that swapping in the label costs no reflow, and it is the
   taller of the two at every width, so the cell's height is the combo's and the label cannot
   move a pixel of the card. The inline padding lines the placeholder up with the rows the field
   fills with. */
.mkt-sc-combo {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: clamp(2.5px, 0.8cqw, 5px) var(--rpad);
  opacity: 0;
}
.mkt-sc-combo-text { flex: 1; min-width: 0; font-size: clamp(9.5px, 2.15cqw, 12px); color: #9AA0B2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mkt-sc-combo-caret {
  flex: 0 0 auto;
  width: 0.42em;
  height: 0.42em;
  font-size: clamp(11px, 2.4cqw, 14px);
  border: solid var(--faint);
  border-width: 0 1.4px 1.4px 0;
  transform: rotate(45deg);
  translate: 0 -0.12em;
}
/* The list's label, in the cell the field's text line vacates when the strategy is saved. It
   takes its face from .mkt-widget-kicker (mono, uppercase, tracked) and only its box from here:
   an explicit line-height, because a grid item's height would otherwise be the flex/grid strut of
   the card's own 14-15px text and 21px tall — taller than the combo above it, which would make
   this label, not the field, the thing that sets the row's height. */
.mkt-sc-label {
  line-height: 1.4;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The picked queries' window: no height of its own at all, so it is what is in it — the helper
   line, then a row per pick, then the three rows the saved card lists. Holding the full --winh
   here from the first frame is what made the card start out as tall as three queries it had not
   been given yet; the card still starts at the height an empty form needs.
   What the window DOES hold, from the moment the picker opens until the last query has landed,
   is the room those queries will take: sc-room pads it out to exactly --winh and hands that
   padding back a row at a time, in step with the rows arriving. So the window's box is one fixed
   number for the whole pick — which is the number the picker is measured against, and the number
   the footer below it is standing on. The card is short while the form is only being typed into
   and opens that room as the picker opens; the room it opens is the room the picker needs. */
.mkt-sc-win { position: relative; }
/* The field's helper text, where MUI prints it: directly under the input, indented to the value
   column, not down in the action row. It is the window's last child and its only content while
   the field is empty, so it is what the empty field's room IS rather than something placed over
   it — and it hands that line to the first picked query, collapsing over exactly the 0.26s that
   row grows. At rest it is gone and takes no room: the saved card has no field to explain, and
   rest is also the no-JS and the reduced-motion frame. */
.mkt-sc-helper {
  margin: 0;
  padding: 0 var(--rpad);
  height: 0;
  overflow: hidden;
  font-size: var(--hint-f);
  line-height: 1.45;
  color: #8A8FA3;
  opacity: 0;
}

/* The popper. It hangs under the field and it is bounded by the field: its lower edge is the
   window's own lower edge plus --popdrop, so it reaches exactly as far as the room the field is
   holding and never a pixel further. That is the whole fix — the edge used to be parked at a
   fixed --winh + 8 below the window's TOP, which was right while the window was a fixed frame
   and wrong the moment the card was shortened: the footer came up 90–113px, the parked edge did
   not, and the listbox dropped straight through "Anuluj / Zapisz" (and, at 390, 8.3px into
   "Dodaj strategię"). Tied to the window instead, the edge stands where the window's padding
   stands, which is where the card's own layout put the seam above the footer.
   Only `top` is animated now: it steps down while the bottom stays, so the open listbox is
   squeezed and shows fewer monitored queries as the field grows, which is what a popper does when
   it runs out of room — and the last step down takes it to the window's own floor, where it has
   no height at all, which is how it closes. An overlay throughout: it costs the card no height,
   and .mkt-sc-pin's own z-index lets it draw over anything it does reach. */
.mkt-sc-pop {
  position: absolute;
  left: calc(-1 * var(--fx));
  right: calc(-1 * var(--fx));
  /* Base is the last stop of the squeeze, as everywhere else here: 2px under the window's floor,
     which with `bottom: -var(--popdrop)` leaves exactly the 6px the listbox's own border and
     padding take and no row at all — a shut box, standing where a shut box's lower edge has
     always stood. The settled card has no picker, and the settled frame is the no-JS and the
     reduced-motion frame. */
  top: calc(var(--winh) + 2px);
  /* --popdrop is 2px (the gap under the field's line, mirrored) + 6px (the listbox's own border
     and padding). The 6px is what makes the box taller than the rows it holds by exactly its own
     chrome, so every stop of the squeeze shows a whole number of whole rows instead of shaving
     4px off the last one's rounded bottom. */
  bottom: calc(-1 * var(--popdrop));
  z-index: 2;
  pointer-events: none;
}
.mkt-sc-listbox {
  height: 100%;
  overflow: hidden;
  padding: 2px;
  border: 1px solid #E4E5F7;
  border-radius: clamp(7px, 1.8cqw, 11px);
  background: #FFFFFF;
  box-shadow: 0 16px 30px -18px rgba(20, 22, 43, 0.45);
  opacity: 0;
}
.mkt-sc-opts { display: flex; flex-direction: column; gap: var(--rgap); }
.mkt-sc-opt {
  flex: 0 0 auto;
  height: var(--row);
  display: flex;
  align-items: center;
  gap: var(--cgap);
  padding: 0 var(--rpad);
  border-radius: clamp(5px, 1.3cqw, 8px);
  font-size: clamp(9.5px, 2.25cqw, 12px);
  color: #4C5163;
}
.mkt-sc-opt.is-picked { background: var(--brand-soft); color: #262A47; }
.mkt-sc-opt-check {
  position: relative;
  flex: 0 0 auto;
  width: 1em;
  height: 1em;
  font-size: clamp(10px, 2.5cqw, 14px);
  border-radius: 0.26em;
  background: #FFFFFF;
  box-shadow: inset 0 0 0 1.5px #C4C8D6;
}
.mkt-sc-opt.is-picked .mkt-sc-opt-check { background: var(--brand); box-shadow: none; }
.mkt-sc-opt-check::after {
  content: "";
  position: absolute;
  left: 0.36em;
  top: 0.19em;
  width: 0.22em;
  height: 0.43em;
  border: solid #FFFFFF;
  border-width: 0 0.115em 0.115em 0;
  transform: rotate(45deg);
  opacity: 0;
}
.mkt-sc-opt.is-picked .mkt-sc-opt-check::after { opacity: 1; }
.mkt-sc-opt-text { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The picked queries, as the saved card lists them: each with the score and position the
   strategy's own numbers are averaged from. The seam is a margin, not the list's `gap`, because a
   row that has not been picked yet is not a row this list has: it comes in from zero height, and
   its seam has to come with it. */
.mkt-sc-queries { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.mkt-sc-q {
  flex: 0 0 auto;
  height: var(--row);
  margin-top: var(--rgap);
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--wscore) var(--wpill);
  align-items: center;
  column-gap: var(--cgap);
  padding: 0 var(--rpad);
  border: 1px solid #DEDFFA;
  border-radius: clamp(6px, 1.7cqw, 10px);
  background: #FBFBFE;
  overflow: hidden;
}
.mkt-sc-q:first-child { margin-top: 0; }
.mkt-sc-q-text { grid-area: 1 / 1; }
.mkt-sc-q-score { grid-area: 1 / 2; }
.mkt-sc .mkt-sc-q .mkt-pill,
.mkt-sc-q-x { grid-area: 1 / 3; }
/* A saved row carries no checkbox — there is nothing left to tick. The app lists a related
   query as a link to that query, so the text is link-coloured and the row is text + numbers. */
.mkt-sc-q-text {
  min-width: 0;
  font-size: clamp(9.5px, 2.25cqw, 12.5px);
  color: var(--brand-dark);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mkt-sc-q-score {
  font-family: 'IBM Plex Mono', monospace;
  font-size: clamp(9px, 2.05cqw, 12px);
  font-weight: 600;
  color: #3F4457;
  text-align: center;
}
.mkt-sc .mkt-sc-q .mkt-pill {
  width: var(--wpill);
  height: var(--wpill);
  border-radius: clamp(5px, 1.2cqw, 8px);
  font-size: clamp(9px, 2.1cqw, 12px);
}
/* The remove affordance a picked value carries while the field is still a field — it sits in the
   position pill's cell, so the two swap without moving anything, and it rests invisible because
   the saved card has no field to remove anything from. */
.mkt-sc-q-x {
  position: relative;
  justify-self: center;
  width: clamp(11px, 2.6cqw, 15px);
  height: clamp(11px, 2.6cqw, 15px);
  border-radius: 50%;
  background: #C9CBF0;
  opacity: 0;
}
.mkt-sc-q-x::before,
.mkt-sc-q-x::after {
  content: "";
  position: absolute;
  left: 27%;
  top: calc(50% - 0.5px);
  width: 46%;
  height: 1px;
  border-radius: 1px;
  background: #FFFFFF;
}
.mkt-sc-q-x::before { transform: rotate(45deg); }
.mkt-sc-q-x::after { transform: rotate(-45deg); }

/* --- what the picked queries add up to ------------------------------------------- */

/* The rule belongs to the SAVED card — a dialog's action row has none, and while the form is open
   this row is exactly that: two buttons and nothing else. Base is the settled state, so the rule
   is here and the sequence holds it transparent until the card settles. */
.mkt-sc-foot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: clamp(6px, 1.5cqw, 10px) clamp(10px, 2.2cqw, 16px);
  padding-top: clamp(6px, 1.8cqw, 12px);
  border-top: 1px solid #F0F1F5;
}
/* The metrics' cell. No min-width: 0 here — it must never be squeezed below the metrics it holds,
   or the average-position pill lands on top of the note next to it. The action instead wraps to
   its own row, which is where the dialog keeps "Anuluj / Zapisz" anyway. */
.mkt-sc-sum { display: grid; flex: 0 1 auto; }
.mkt-sc-sum > * { grid-area: 1 / 1; align-self: center; }
.mkt-sc-stats { display: flex; align-items: center; gap: clamp(8px, 2.2cqw, 16px); min-width: 0; }
.mkt-sc-metric { display: flex; align-items: center; gap: clamp(6px, 1.6cqw, 10px); min-width: 0; }
.mkt-sc-ring { position: relative; flex: 0 0 auto; width: clamp(28px, 7.6cqw, 46px); height: clamp(28px, 7.6cqw, 46px); }
.mkt-sc-ring svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
.mkt-sc-ring circle { fill: none; stroke-width: 6; }
.mkt-sc-ring .is-track { stroke: #F1F2F6; }
.mkt-sc-ring .is-value { stroke: var(--ok); stroke-linecap: round; stroke-dasharray: 100; stroke-dashoffset: calc(100 - var(--score)); }
.mkt-sc-ring .mkt-sc-num {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: clamp(10px, 2.6cqw, 16px);
  font-weight: 650;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.mkt-sc .mkt-sc-avg {
  width: auto;
  min-width: clamp(24px, 6cqw, 36px);
  height: clamp(22px, 5.4cqw, 33px);
  padding: 0 0.42em;
  border-radius: clamp(6px, 1.8cqw, 10px);
  font-size: clamp(10.5px, 2.5cqw, 15px);
}
.mkt-sc-metric-copy { min-width: 0; }
.mkt-sc-metric-copy strong {
  display: block;
  font-size: clamp(9.5px, 2.1cqw, 12.5px);
  font-weight: 600;
  color: #262A47;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Form buttons and the saved card's action share the second row, the same way. */
.mkt-sc-actions { flex: 1 0 auto; display: grid; justify-items: end; }
.mkt-sc-actions > * { grid-area: 1 / 1; }
.mkt-sc-save { display: inline-flex; align-items: center; gap: 6px; opacity: 0; }
.mkt-sc-btn,
.mkt-sc-cta {
  font-size: clamp(9.5px, 2.1cqw, 12.5px);
  font-weight: 650;
  white-space: nowrap;
  padding: clamp(4px, 1.1cqw, 8px) clamp(7px, 2cqw, 13px);
  border-radius: clamp(7px, 1.6cqw, 10px);
}
.mkt-sc-btn.is-ghost { color: var(--muted); }
.mkt-sc-btn.is-save { background: var(--brand); color: #FFFFFF; box-shadow: 0 8px 16px -10px rgba(91, 92, 230, 0.9); }
.mkt-sc-cta {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  border: 1px solid var(--brand-line);
  background: #FFFFFF;
  color: var(--brand-dark);
}
.mkt-sc-cta svg { width: 1.15em; height: 1.15em; flex: 0 0 auto; }

/* --- the rest of the list ------------------------------------------------------- */

.mkt-sc-card.is-peek {
  display: flex;
  align-items: center;
  gap: clamp(8px, 2cqw, 14px);
  padding: clamp(7px, 1.8cqw, 13px) clamp(9px, 2.3cqw, 17px);
  background: #FCFCFE;
}
.mkt-sc-peek-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.mkt-sc-peek-name {
  margin: 0;
  min-width: 0;
  font-size: clamp(10.5px, 2.3cqw, 14px);
  font-weight: 600;
  color: #262A47;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Two lines at every width, not just on the roomy previews. The right-hand side of this card
   carries a sentence now instead of two 25px slots, so a one-line intent would have been cut
   mid-word at the widths where it used to just fit — and a collapsed strategy whose one line of
   description ends in an ellipsis tells the reader less than the card costs. The second line is
   ~15px of card, and the card above it has ~27px more to give since its header row went. */
.mkt-sc-peek-intent {
  margin: 0;
  font-size: clamp(9.5px, 2cqw, 12px);
  line-height: 1.4;
  color: #7C8196;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.mkt-sc-peek-side { flex: 0 0 auto; display: flex; align-items: center; gap: clamp(7px, 1.8cqw, 12px); }
/* Why the second strategy carries no numbers, said in words. It replaces the two dashed slots
   that stood here — a ring and a pill with an en dash in each — which read as two greyed-out
   buttons rather than as an empty score, and which kept their meaning in a `title` that no touch
   screen ever shows. `flex: 0 1 auto` with min-width: 0, so on the narrowest card that still
   shows this row the sentence ellipsises rather than pushing the name beside it out of the box;
   it never has to, at any width the page renders. */
.mkt-sc-peek-empty {
  flex: 0 1 auto;
  min-width: 0;
  font-size: clamp(9px, 1.9cqw, 11.5px);
  line-height: 1.3;
  color: #9AA0B2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mkt-sc-add {
  display: flex;
  align-items: center;
  gap: clamp(6px, 1.5cqw, 9px);
  border: 1.5px dashed var(--brand-line);
  border-radius: clamp(9px, 2.1cqw, 14px);
  padding: clamp(6px, 1.6cqw, 11px) clamp(9px, 2.2cqw, 15px);
  background: var(--brand-soft);
  color: #3D3FCF;
  font-size: clamp(10px, 2.1cqw, 13px);
  font-weight: 650;
}
.mkt-sc .mkt-wiz-add-plus { width: clamp(15px, 3.2cqw, 21px); height: clamp(15px, 3.2cqw, 21px); }

/* The tablet column (~660px square) is the roomiest frame the widget ever gets, and at the
   desktop caps the content stopped growing long before the box did — the spare height ended up
   in the seams between the sections (three 41px bands of nothing). Here the content itself takes
   the room: taller rows, a bigger gauge, larger type — the density of a real 660px-wide card,
   not a desktop card stretched apart. Every step is still driven by the same custom properties,
   so the picker's scroll (--step) and the score column's alignment follow by themselves. */
@container sc (min-width: 600px) {
  /* --row on the card and the add row, never on .mkt-sc: an element is never matched against its
     own container. Both, because the room the card grows into is held in the add row's margin. */
  .mkt-sc-card.is-new,
  .mkt-sc-add {
    --row: clamp(30px, 5.75cqw, 40px);
    --rgap: clamp(6px, 1.3cqw, 9px);
    --hint-f: clamp(11.5px, 1.95cqw, 13px);
  }
  .mkt-sc-card.is-new {
    --rpad: clamp(11px, 2cqw, 15px);
    --cgap: clamp(8px, 1.6cqw, 12px);
    --wscore: clamp(26px, 4.8cqw, 34px);
    --wpill: clamp(24px, 4.7cqw, 32px);
    /* The seam floor again (see the base rule): at the ~605px preview where this roomier set
       starts, the footer wraps its action under the metrics and the card has nothing to spare. */
    gap: clamp(7px, 1.25cqw, 17px);
    padding: clamp(15px, 2.6cqw, 21px) clamp(17px, 2.9cqw, 23px);
  }
  .mkt-sc-name { font-size: clamp(16px, 3.1cqw, 20px); }
  .mkt-sc-field { padding-bottom: clamp(6px, 1.15cqw, 8px); }
  .mkt-sc-tool { width: clamp(26px, 4.4cqw, 32px); height: clamp(26px, 4.4cqw, 32px); border-radius: 9px; }
  .mkt-sc-intent { font-size: clamp(13px, 2.25cqw, 14.5px); }
  .mkt-sc-pin { --pgap: clamp(7px, 1.55cqw, 10.5px); }
  .mkt-sc .mkt-widget-kicker { font-size: clamp(10.5px, 1.75cqw, 11.5px); }
  /* Block only: the inline padding stays --rpad, so the placeholder keeps lining up with the
     rows the field fills with. */
  .mkt-sc-combo { padding-block: clamp(5px, 0.95cqw, 7px); }
  .mkt-sc-combo-text { font-size: clamp(12px, 2.1cqw, 14px); }
  .mkt-sc-opt { font-size: clamp(12px, 2.2cqw, 14.5px); }
  .mkt-sc-opt-check { font-size: clamp(14px, 2.4cqw, 17px); }
  .mkt-sc-q-text { font-size: clamp(12.5px, 2.2cqw, 14.5px); }
  .mkt-sc-q-score { font-size: clamp(12px, 2cqw, 13.5px); }
  .mkt-sc-foot { padding-top: clamp(12px, 2.5cqw, 17px); }
  .mkt-sc-ring { width: clamp(46px, 8.4cqw, 56px); height: clamp(46px, 8.4cqw, 56px); }
  .mkt-sc-ring .mkt-sc-num { font-size: clamp(16px, 2.8cqw, 19px); }
  .mkt-sc .mkt-sc-avg { min-width: clamp(36px, 5.9cqw, 40px); height: clamp(33px, 5.7cqw, 38px); font-size: clamp(15px, 2.65cqw, 18px); }
  .mkt-sc-metric-copy strong { font-size: clamp(12.5px, 2.1cqw, 14px); }
  .mkt-sc-btn,
  .mkt-sc-cta { font-size: clamp(12.5px, 2.1cqw, 14px); padding: clamp(8px, 1.5cqw, 11px) clamp(13px, 2.3cqw, 16px); }
  /* The rest of the list grows with it; the second strategy's intent gets the second line it
     needs at this width instead of being cut mid-word. */
  .mkt-sc-card.is-peek { padding: clamp(11px, 2.05cqw, 15px) clamp(13px, 2.4cqw, 18px); }
  .mkt-sc-peek-name { font-size: clamp(14px, 2.2cqw, 15.5px); }
  .mkt-sc-peek-intent { font-size: clamp(12px, 1.9cqw, 13.5px); }
  .mkt-sc-peek-empty { font-size: clamp(11.5px, 1.85cqw, 13px); }
  .mkt-sc-add { font-size: clamp(13px, 2cqw, 14.5px); padding: clamp(11px, 1.9cqw, 14px) clamp(15px, 2.4cqw, 18px); }
  .mkt-sc .mkt-wiz-add-plus { width: clamp(21px, 3.2cqw, 24px); height: clamp(21px, 3.2cqw, 24px); }
}

/* Desktop-column previews (~350–560px square): the card's share of the square is the tightest
   here — at 1024 the content wanted 17px more than the box and clipped the CTA. The seams and the
   field paddings give it back; nothing is dropped. The ~700px tablet column keeps the roomier set. */
@container sc (max-width: 560px) {
  .mkt-sc-card.is-new { gap: clamp(3.5px, 1cqw, 10px); padding: clamp(8px, 2.1cqw, 14px) clamp(9px, 2.4cqw, 17px); }
  .mkt-sc-pin { --pgap: clamp(3px, 1.05cqw, 7px); }
  .mkt-sc-combo { padding-block: clamp(1.5px, 0.6cqw, 4px); }
  .mkt-sc-foot { padding-top: clamp(5px, 1.6cqw, 10px); row-gap: clamp(4px, 1.2cqw, 8px); }
}

/* The two narrowest squares (the 1024 desktop column at ~456px and the phone at ~348px) are
   tighter still: the rows, the gauge and the paddings each give up a couple of pixels so the
   card keeps every part of the story — including the action under the metrics. */
@container sc (max-width: 470px) {
  /* --row is redeclared on the card and the add row, not on .mkt-sc: an element is never matched
     against its own container, so a rule for .mkt-sc inside @container sc would never apply. */
  .mkt-sc-card.is-new,
  .mkt-sc-add { --row: clamp(19px, 4.6cqw, 32px); }
  /* ONE ramp for the whole 381-470 range, floor included, and it is the ramp the rung above
     carries on (2.1cqw, 9.89px at the 471px preview where `max-width: 560px` takes over). The
     block padding used to be shaved twice on the way down — 1.9cqw here and 1.55cqw below 430 —
     and the second shave took it UNDER the flat 7px the phone rung keeps, so the card's bottom
     edge got thinner exactly as the footer wrapped its action under the metrics and the action
     landed flush on it: 6px of air under "Rekomendacje artykulow" at a 381-398px container
     (viewport 965-999) against 8.3px at 431 and 10.4px at 471. The floor is 8.5px because below
     a ~405px container the ramp itself falls under it, and 8.5 is what keeps the button's own
     clearance (9.5px to the border) in line with both neighbours. The room comes from the card's
     own space-between air, which is 12-33px across this range, not from any section. */
  .mkt-sc-card.is-new { padding-block: clamp(8.5px, 2.1cqw, 12px); }
  .mkt-sc-ring { width: clamp(26px, 7cqw, 40px); height: clamp(26px, 7cqw, 40px); }
  .mkt-sc-foot { padding-top: clamp(4px, 1.4cqw, 8px); }
}

/* The smallest square the WHOLE card is ever asked to fill — ~420px, which the page hands this
   widget twice: at a 455-500 viewport, where the two-column grid has not split yet and the column
   is narrow, and again at 965-999, where it has split and each column is barely wider. Below this
   the phone rung takes the second strategy and the form's buttons out of the frame; here every
   part of the story stays, so the paddings INSIDE the card give up the last pixels instead. Each
   is under a pixel and a half, and they are the ones the eye cannot get back from space-between:
   the seams around the footer's rule and around the field's label, and the buttons' own height.
   The card's own edge is NOT one of them any more — it is the one padding the eye reads against
   the button sitting on it, so it stays on the 381-470 ramp above. (The card's section seams are
   not here either — they are a floor, see .mkt-sc-card.is-new.) */
@container sc (max-width: 430px) {
  /* The seam floor, lowered with them. It is a floor and nothing else — space-between owns the
     seam for as long as the card has a pixel to spare — so lowering it changes no measurement at
     any width the page renders (swept at 1px from 380 to 1100 and 1380 to 1500 in both languages:
     every box identical), and it only decides what gives FIRST when the card runs out. The
     tightest square in the widget is the 409px preview at a viewport of 451, and there the card
     now has 6.8px of content growth in hand instead of 4.5px; past that the seams close from
     5.8px towards 3.2px before anything reaches the card's bottom edge, where the action row is
     standing. */
  .mkt-sc-card.is-new { gap: clamp(3px, 0.8cqw, 10px); }
  .mkt-sc-pin { --pgap: clamp(2.5px, 0.8cqw, 7px); }
  .mkt-sc-foot { padding-top: clamp(3.5px, 1.1cqw, 8px); row-gap: clamp(3px, 0.9cqw, 8px); }
  .mkt-sc-btn,
  .mkt-sc-cta { padding-block: clamp(3.2px, 0.85cqw, 8px); }
}

/* Phone-sized preview: the square is only ~290px tall inside its chrome. What must survive is the
   list — a strategy AND the row that starts the next one, which is the whole shape of the view —
   so the height comes out of the card instead: one line of intent, shorter rows, a smaller gauge.
   Only the second strategy, the form's buttons and the row tools step out of the frame. */
@container sc (max-width: 380px) {
  .mkt-sc-tools { display: none; }
  .mkt-sc-card.is-new,
  .mkt-sc-add { --row: clamp(21px, 5.4cqw, 32px); --rgap: 5px; }
  .mkt-sc-card.is-new { gap: 4px; padding: 7px 9px; }
  .mkt-sc-intent { -webkit-line-clamp: 1; line-height: 1.4; }
  .mkt-sc-foot { padding-top: 5px; }
  .mkt-sc-actions { display: none; }
  .mkt-sc-ring { width: 23px; height: 23px; }
  .mkt-sc-ring circle { stroke-width: 7; }
  .mkt-sc .mkt-sc-avg { height: 20px; min-width: 22px; font-size: 10px; }
  .mkt-sc-card.is-peek { display: none; }
  .mkt-sc-add { padding: 5px 9px; }
  /* The frame keeps more of the square here, not less. With two of the three blocks gone the card
     has room to spare whatever the frame does with it, and spare room in this card is air between
     its sections (space-between) — so the padding and the seam take their share back rather than
     handing another 16px to seams that are already the widest thing in the frame. This is the
     rung that a ~420px preview lands on at a viewport of exactly 960, where the body's own
     padding is a desktop's 20px and the container reads 378. */
  .mkt-sc::before { height: clamp(14px, 4.5cqw, 18px); }
  .mkt-sc::after { height: clamp(14px, 5.1cqw, 20px); }
  .mkt-sc > * + * { margin-top: clamp(6px, 2.9cqw, 11px); }
}

/* --- sequence --------------------------------------------------------------------- */

/* The form: the card holds the "Nowa strategia" fields until it is saved. */
.mkt-sc.is-armed .mkt-sc-card.is-new { animation: sc-settle 0.45s ease 3.60s backwards; }
/* THE CARD'S GROWTH, held at the foot of the list. The card is flex: 1 1 auto in a square of
   fixed height, so it is exactly what the square has left over: park the room its three growths
   will take in the LAST row's bottom margin and the card is that much shorter, the list is that
   much higher up the square, and handing the room back a piece at a time grows the card by
   exactly one landing row each time. Same clock and same curve as the rows themselves, so the
   box and the row inside it are one movement and never a reflow chasing an animation.

   The room is held at the foot rather than between the card and the rest of the list, because a
   list with a band of nothing inside it reads as a broken frame, while a list that does not yet
   reach the bottom of the view reads as a list — which is exactly what it is, until the strategy
   being written is finished. The price is that the second strategy and the add row ride down with
   each landing; they ride down ON the landing, on its curve and in its 0.26s, so what the eye
   sees is the list growing rather than the layout shifting. */
.mkt-sc.is-armed .mkt-sc-add { animation: sc-grow 3.3s cubic-bezier(0.2, 0.85, 0.3, 1) 0s both; }
/* ...and the same room, once it is the card's, held INSIDE the window as its padding. Same clock
   and same curve as the rows, so from 1.30 on the window's box is exactly --winh at every
   instant: the padding gives back precisely what each arriving row takes. That fixed box is what
   the footer stands on and what the picker is measured against, so neither of them moves for the
   whole pick and the picker cannot grow past the seam above the buttons. */
.mkt-sc.is-armed .mkt-sc-win { animation: sc-room 3.3s cubic-bezier(0.2, 0.85, 0.3, 1) 0s both; }
.mkt-sc.is-armed .mkt-sc-field { animation: sc-field 0.4s ease 3.60s backwards; }
.mkt-sc.is-armed .mkt-sc-intent { animation: sc-textarea 0.4s ease 3.60s backwards; }
.mkt-sc.is-armed .mkt-sc-tools { animation: sc-fade-in 0.3s ease 3.72s backwards; }
/* A dialog's action row carries no rule; the saved card's footer does. */
.mkt-sc.is-armed .mkt-sc-foot { animation: sc-foot-rule 0.4s ease 3.60s backwards; }

/* The name and the intent are written briskly, and they are written to a deadline: the picker
   opens at 1.26 and the room it fills has been standing empty since the card appeared, so every
   tenth of a second spent before it is a tenth of the story spent on a hole. */
.mkt-sc.is-armed .mkt-sc-name-text { animation: sc-type 0.65s steps(38) 0.2s backwards; }
.mkt-sc.is-armed .mkt-sc-caret {
  animation:
    sc-caret-move 0.65s steps(38) 0.2s backwards,
    sc-caret-on 0.9s linear 0.05s both;
}
.mkt-sc.is-armed .mkt-sc-chars > i { animation: sc-fade-in 0.15s linear calc(0.84s + var(--c) * 0.0024s) backwards; }
/* 0.43 -> 1.16: the helper is read from the moment the field is drawn until the picker opens over
   it, and it is GONE by the time the box has anything in it — it used to still be fading at 1.29,
   which drew a ghost line across the listbox's first row for two frames. Its LINE, which is the
   empty field's whole room, it keeps until 1.51 and then gives to the first picked query —
   collapsing over exactly the 0.26s that row grows, on the row's own curve, so the card gains
   (row - line) in one movement instead of dipping and rising again. */
.mkt-sc.is-armed .mkt-sc-helper {
  animation:
    sc-hint 0.86s linear 0.3s both,
    sc-hint-room 0.26s cubic-bezier(0.2, 0.85, 0.3, 1) 1.51s both;
}

/* The field. It opens EMPTY — its outline hugs nothing but the placeholder line, with the helper
   text below it where MUI prints it — and from the first pick on it hugs the picked queries
   themselves. The row landings' own curve, not the picker's: the outline's job is to stay on the
   rows, so it has to move exactly as they do. */
.mkt-sc.is-armed .mkt-sc-box::before {
  animation:
    sc-form-out 3.55s linear 0.15s both,
    sc-field-grow 3.3s cubic-bezier(0.2, 0.85, 0.3, 1) 0s both;
}
.mkt-sc.is-armed .mkt-sc-combo { animation: sc-form-out 3.55s linear 0.15s both; }
.mkt-sc.is-armed .mkt-sc-label { animation: sc-fade-in 0.35s ease 3.66s backwards; }

/* The picker: it hangs under the field and is pinned to the footer, so a pick that makes the
   field taller squeezes it. It has exactly two squeezes in it and it takes ONE — three monitored
   queries in view, then two — and the second squeeze is its exit: the last two values land
   together (3.02–3.28) and the box is shut by the same movement that makes room for them.
   Two frames are gone with that, and both of them were the same mistake — a box that had stopped
   being a dropdown but was still on screen, or a room that had stopped being a dropdown's and was
   not yet anything else:
   • the ONE-ROW BOX. The lower edge is the window's own, so the box is `--winh + --popdrop`
     minus its top, and its top steps down a row per landing: three rows, two rows, one row. That
     third stop is a single white row standing under the field with the action row below it, and
     it is the frame the owner objected to — not for how long it lasted (0.72s, then a measured
     0.45s after the last round shortened it; shortening was the wrong axis) but for its SHAPE.
     The stop is
     gone now, not shortened: the top leaves `--step + 2px` and does not stop again until the box
     has no height at all, so the box is two rows, then closing, and is never one row.
   • the EMPTY BAND. The room under the field belongs to the picker until the rows take it, and
     the two used to hand over 0.30s apart — the box went at 2.72, the third row only started
     growing at 3.02, and in between the card showed the field, a band of nothing and then
     "Anuluj / Zapisz" (measured 26px at 390, 24 at 455, 43.8 at 768). Now the two are one
     movement on one clock: the
     box's top runs down exactly as the rows grow, so the gap between the last row and the box's
     edge is the dropdown's own seam — --rgap + 2px — at every instant of the close, and 0 once
     the rows are in. Nothing is ever uncovered.
   Both edges therefore ride the rows' own curve, not one of their own: cubic-bezier(0.2, 0.85,
   0.3, 1) here and on the scroll below is the curve sc-room and sc-row-in use, which is what
   makes "exactly as" exact rather than approximate. */
.mkt-sc.is-armed .mkt-sc-pop { animation: sc-pop-squeeze 3.3s cubic-bezier(0.2, 0.85, 0.3, 1) 0s both; }
.mkt-sc.is-armed .mkt-sc-listbox { animation: sc-listbox 2.18s ease 1.1s both; }
/* Same curve as the squeeze above, and the same clock for the stops they share: the list rises by
   exactly the row the popper loses, at exactly the same moment, so the option that has just been
   ticked slides out under the popper's own edge instead of being cut in half by it, and the
   options still in view do not move at all. Both moves are that compensation, not a scroll going
   anywhere: the three queries the strategy is built from are the first three options, and after
   the one squeeze the two still to be ticked are the two rows on screen. */
.mkt-sc.is-armed .mkt-sc-opts { animation: sc-scroll 2.02s cubic-bezier(0.2, 0.85, 0.3, 1) 1.26s both; }
.mkt-sc.is-armed .mkt-sc-opt.is-picked { animation: sc-opt-pick 0.3s ease calc(var(--t) * 1s) backwards; }
.mkt-sc.is-armed .mkt-sc-opt.is-picked .mkt-sc-opt-check { animation: sc-check 0.32s cubic-bezier(0.2, 0.9, 0.3, 1.3) calc(var(--t) * 1s) backwards; }
/* The tick must finish drawing while its box is still fully opaque: the last is drawn by 2.84
   and the listbox only starts to leave at 3.02. */
.mkt-sc.is-armed .mkt-sc-opt.is-picked .mkt-sc-opt-check::after { animation: sc-fade-in 0.16s ease calc((var(--t) + 0.06) * 1s) backwards; }

/* The picked queries: a row comes into existence when its value lands — from no height and no
   seam at all, so the list is never a set of empty fields waiting to be filled. The first lands
   as it is ticked; the last two land together at 3.02, in the room the picker gives back by
   closing over them, which is why they share a --r. Until the strategy is saved a row is the
   value a chip holds in the app's field, remove affordance and all; at 3.60 it settles into the
   saved card's row and the numbers arrive with the metrics. */
.mkt-sc.is-armed .mkt-sc-q {
  animation:
    sc-row-in 0.26s cubic-bezier(0.2, 0.85, 0.3, 1) calc(var(--r) * 1s) backwards,
    sc-chip 0.4s ease 3.6s backwards;
}
.mkt-sc.is-armed .mkt-sc-q-text { animation: sc-fade-in 0.3s ease calc(var(--r) * 1s) backwards; }
/* One animation per row rather than a fade in and a fade out, so the three crosses leave together
   with the rest of the form at 3.70: the delay moves, the duration makes up the difference. */
.mkt-sc.is-armed .mkt-sc-q-x { animation: sc-chip-x calc((3.62 - var(--r)) * 1s) linear calc((var(--r) + 0.08) * 1s) both; }
.mkt-sc.is-armed .mkt-sc-q .mkt-pill { animation: sc-pill 0.32s cubic-bezier(0.2, 0.9, 0.3, 1.3) calc(3.82s + var(--k) * 0.09s) backwards; }

/* Saving, then the numbers the strategy is worth. "Zapisz" goes live as soon as the strategy has
   a name — the app disables it on nothing else — and is pressed once the third query is in the
   field (3.42–3.64); the form parts only start dissolving when the press is over. */
.mkt-sc.is-armed .mkt-sc-save { animation: sc-form-out 3.63s linear 0.15s both; }
.mkt-sc.is-armed .mkt-sc-btn.is-save { animation: sc-enable 0.3s ease 0.95s backwards, sc-press 0.22s ease 3.42s; }

.mkt-sc.is-armed .mkt-sc-q-score { animation: sc-rise 0.3s ease calc(3.78s + var(--k) * 0.09s) backwards, sc-count 0.5s linear calc(3.78s + var(--k) * 0.09s) both; }
/* 3.62, not 3.66: the metrics rise as the card settles under them, not after it, so the footer
   turns from an action row into a metrics row in one movement. */
.mkt-sc.is-armed .mkt-sc-stats { animation: sc-rise 0.35s ease 3.62s backwards; }
/* The gauge is the mean of the three query scores, so it must not sprint ahead of the numbers it
   averages: an eased fill read 68 while its own inputs still averaged 34. Linear, over exactly
   the window the three scores count in (3.78 → 4.46), the ring finishes as they do and stays
   close to their running mean the whole way. Arc and number share the curve so they never split. */
.mkt-sc.is-armed .mkt-sc-ring .is-value { animation: sc-ring 0.68s linear 3.78s both; }
.mkt-sc.is-armed .mkt-sc-ring .mkt-sc-num { animation: sc-count 0.68s linear 3.78s both; }
.mkt-sc.is-armed .mkt-sc-avg { animation: sc-pill 0.36s cubic-bezier(0.2, 0.9, 0.3, 1.3) 4.24s backwards; }
.mkt-sc.is-armed .mkt-sc-cta { animation: sc-rise 0.3s ease 4.38s backwards; }

/* Waiting for marketing.js to mark the widget live: hold the first frame. */
.mkt-widget:not(.is-live) .mkt-sc.is-armed *,
.mkt-widget:not(.is-live) .mkt-sc.is-armed *::before,
.mkt-widget:not(.is-live) .mkt-sc.is-armed *::after { animation-play-state: paused !important; }

@media (prefers-reduced-motion: reduce) {
  .mkt-sc *,
  .mkt-sc *::before,
  .mkt-sc *::after { animation: none !important; }
}

@keyframes sc-rise { from { opacity: 0; translate: 0 4px; } }
@keyframes sc-fade-in { from { opacity: 0; } }
/* A form part: in early, held at full opacity until very late, then out quickly — so the press
   lands on a solid button and the parts leave only once they have been acted on. */
@keyframes sc-form-out { 0% { opacity: 0; } 5%, 96% { opacity: 1; } 100% { opacity: 0; } }
@keyframes sc-settle { from { border-color: var(--brand-line); border-style: dashed; background: #F8F9FE; } }
/* No `to`: the rule of the settled card is the base style. */
@keyframes sc-foot-rule { from { border-top-color: transparent; } }
/* The helper: in as the field is drawn, out as the picker opens over it — out by 1.16, when the
   listbox starts to unfurl, not while it is already drawn. */
@keyframes sc-hint { 0% { opacity: 0; } 15%, 84% { opacity: 1; } 100% { opacity: 0; } }
/* ...and its line, which is the room the empty field has, handed to the first picked query. No
   `to`: the end state is the base style — gone, and taking no room — so the two cannot drift. */
@keyframes sc-hint-room { from { height: var(--hint-h); } }
/* The room the card grows into, held under the list until the card needs it — which is the
   moment the picker opens, not the moment the rows land. While the strategy is only being named
   and described the card has nothing to put there, so the room stays at the foot and the card
   stays short: 1.10-1.30 (33.33-39.39% of the 3.3s clock) it moves inside, the card opens by
   exactly what the picker is about to take, and the rest of the list rides down once instead of
   three times. From 1.26 on this is 0 and the card's box does not change again — every later
   growth is the window handing its own padding back (sc-room). No `to` value that is not the
   base 0: the settled frame is the no-JS and the reduced-motion frame. */
@keyframes sc-grow {
  0%, 33.33% { margin-bottom: calc(var(--winh) - var(--hint-h)); }
  39.39%, 100% { margin-bottom: 0; }
}
/* The same room seen from inside the window: padding under the picked queries, so the window's
   box is --winh from 1.30 to the end however few rows are in it. The stops are the row landings
   — 1.51-1.77 for the first, 3.02-3.28 for the other two together — and the values are what the
   window is still missing at each: the helper's line standing in for the first row, then two
   rows, then none. Each is the exact complement of what the rows and the collapsing helper add
   over the same window and the same curve, so the sum never wavers by a pixel.
   There is no one-row stop any more, because there is no frame with two rows in the field: the
   last two values land together, in one 0.26s, and the picker closes over exactly that window.
   That is what keeps this padding covered from the moment it is opened to the moment it is
   spent — it is the picker's floor while the picker is there and the rows' the instant it is
   not, with nothing in between. */
@keyframes sc-room {
  0%, 33.33% { padding-bottom: 0; }
  39.39%, 45.76% { padding-bottom: calc(var(--winh) - var(--hint-h)); }
  53.64%, 91.52% { padding-bottom: calc(2 * var(--step)); }
  99.39%, 100% { padding-bottom: 0; }
}
@keyframes sc-field { from { border-bottom-color: var(--brand-line); } }
@keyframes sc-textarea { from { background: #FFFFFF; box-shadow: 0 0 0 7px #FFFFFF; } }
@keyframes sc-type { from { clip-path: inset(0 100% 0 0); } }
@keyframes sc-caret-move { from { left: 0; } }
@keyframes sc-caret-on { 0% { opacity: 1; } 6% { opacity: 0; } 13%, 95% { opacity: 1; } 100% { opacity: 0; } }
/* 1.10-1.24 open, 3.02-3.28 closed — and the close is the last two values landing. The box is
   drawn over the field, so while it fades its own rows and the chips arriving underneath could
   print the same sentence twice, 3px apart, which is what four frames of an earlier version did.
   They cannot now: the box's top edge stays --rgap + 2px BELOW the last row at every instant of
   the close (sc-pop-squeeze rides the rows' curve), so the box is only ever over the room the
   rows have not reached yet, and its own rows are clipped away above that edge.
   The opening starts on the same tick as the room and on the room's own curve, which is the only
   way the room is never uncovered at THIS end either. The box's geometry already follows the
   window exactly — its lower edge is the window's own — so the room and the box grow together;
   what used to lag was the opacity. It began 0.06s after the room did (1.16 against 1.10) and
   climbed on `ease` while the room climbed on a front-loaded curve, so the room stood open and
   empty for six hundredths of a second — 48px at a 348px preview, 85px at 702px — and was still
   only half covered at 1.20. Now the fade starts at 1.10 and rides cubic-bezier(0.2, 0.85, 0.3,
   1), the curve sc-room opens on, and finishes at 1.24, six hundredths BEFORE the room does: the
   box is never less opaque than the room is open.
   The stops are 0.14s in and 0.26s out of the 2.18s the box is on screen; the out stop keeps the
   shorthand's `ease` and its own two instants (3.02 and 3.28) unchanged, and is a plain fade
   because the box is collapsing geometrically at the same time, so a scale on top of that only
   slid its clipped rows by a fraction of a pixel. */
@keyframes sc-listbox {
  0% { opacity: 0; scale: 1 0.9; translate: 0 -5px; animation-timing-function: cubic-bezier(0.2, 0.85, 0.3, 1); }
  6.42%, 88.07% { opacity: 1; scale: 1 1; translate: 0 0; }
  100% { opacity: 0; }
}
/* The list rises by exactly the row the popper loses, at exactly the same moment and on the same
   curve, so the rows still in view do not move at all: three of them while the field is empty,
   two from the first pick on. Neither move is a scroll in the sense of going somewhere — each is
   the compensation that keeps the remaining queries standing still while the box shrinks around
   them, and what leaves the window is always the query that has just become a chip in the field
   above.
   Both stops are holds that span their ticks. 1.48 is ticked in the three-row box on the row
   that then becomes the first chip; 2.10 and 2.62 are ticked in the two-row box, on the two rows
   the one compensation leaves standing — which are exactly the two queries still to be picked,
   because the strategy is built from the first three options in the list. Nothing is ever ticked
   on a moving row, and no row on screen is ever one the field already prints as a chip.
   The last move is the close: the box loses both its rows at once, so the list rides up by two
   more steps as the top edge comes down over it, and the rows on screen stand still until the
   edge reaches them. (The edge travels 2*--step - --rgap + 6px there, the two rows plus the box's
   own chrome; the list travels 2*--step. The 1-2px between the two is inside a box that is
   fading out and is clipped to nothing by the end of the same 0.26s.) */
@keyframes sc-scroll {
  0%, 12.38% { translate: 0 0; }
  25.25%, 87.13% { translate: 0 calc(var(--step) * -1); }
  100% { translate: 0 calc(var(--step) * -3); }
}
@keyframes sc-opt-pick { from { background: transparent; color: #4C5163; } }
@keyframes sc-check { from { background: #FFFFFF; box-shadow: inset 0 0 0 1.5px #C4C8D6; } 55% { scale: 1.2; } }
/* A row arriving in the field: no height, no seam, no borders — it is not there at all. */
@keyframes sc-row-in { from { height: 0; margin-top: 0; border-block-width: 0; opacity: 0; } }
/* ...and what it looks like until the strategy is saved: the fill a picked value has in the field. */
@keyframes sc-chip { from { background: var(--brand-soft); border-color: #D9DAF8; } }
@keyframes sc-chip-x { 0% { opacity: 0; } 14%, 88% { opacity: 1; } 100% { opacity: 0; } }
/* The field's outline. `bottom` is measured up from the window's bottom edge, and the window now
   holds the room the picks will take as padding, so the outline has to step down over that
   padding as it is handed back: while the field is empty it ends 2px past the text line, and
   from each landing on it ends 2px past the last picked row. Same stops and same curve as
   sc-room and the rows themselves, so the outline hugs what is in the field at every instant
   rather than chasing it. The last stop is the base -2px: the settled window is the three rows
   and nothing else. */
@keyframes sc-field-grow {
  0%, 33.33% { bottom: calc(var(--hint-h) + var(--pgap) - 2px); }
  39.39%, 45.76% { bottom: calc(var(--winh) + var(--pgap) - 2px); }
  53.64%, 91.52% { bottom: calc(2 * var(--step) - 2px); }
  99.39%, 100% { bottom: -2px; }
}
/* The popper follows the field down on the same clock, measured from the window's top, which is
   where the field's own line ends and the only edge in the block that stands still. Its lower
   edge is not in here at all any more: it is `bottom: -var(--popdrop)` on the element, the
   window's own edge, so every step down is a row lost and the box can never be taller than the
   room the field is holding.
   Three stops, and the third is not a stop the box rests at — it is the floor of the window
   itself, where the box has no height left. It takes the first landing as a squeeze (three rows
   to two) and the last as its exit (two rows to none), so the one-row box that used to stand
   between them never happens: --winh + --popdrop minus 2*--step - 2px is one row exactly, by
   construction, at every width, which is why it had to go as a stop rather than be made shorter.
   The third value is --winh + 2px and not --winh + --popdrop, so what is left at the end is the
   6px of border and padding the listbox cannot shed — and that 6px ends on the lower edge the
   open box had, instead of 6px past it and into the seam above the buttons.
   The last two values land over the same 0.26s this edge travels, on the rows' own curve, so the
   edge is --rgap + 2px under the last row the whole way down and the window's padding is never
   uncovered. */
@keyframes sc-pop-squeeze {
  0%, 45.76% { top: 2px; }
  53.64%, 91.52% { top: calc(var(--step) + 2px); }
  99.39%, 100% { top: calc(var(--winh) + 2px); }
}
@keyframes sc-pill { from { opacity: 0; scale: 0.55; } }
@keyframes sc-enable { from { background: #E7E8EE; color: #9AA0B2; box-shadow: none; } }
@keyframes sc-press { 50% { scale: 0.94; background: var(--brand-dark); } }
@keyframes sc-count { from { --sc-n: 0; } }
@keyframes sc-ring { from { stroke-dashoffset: 100; } }

/* === task-board ============================================================ */
/*
 * Task Manager (kanban) — widgets/v2/task-board.blade.php, step 3 of "Jak to działa".
 *
 * Every rule below describes the SETTLED board (2 / 2 / 2 tasks); the sequence that leads to it is
 * a set of keyframe animations whose early frames hold the earlier arrangement. They only apply
 * once the widget is armed by its inline script, sit paused until marketing.js marks the widget
 * live, and restart by themselves each time the accordion shows the widget again.
 *
 * Because every card is the same height, a card's neighbouring slot is exactly its own size plus a
 * gap away: one column = translate(100% + column gap), one row = translate(0, 100% + card gap).
 * That is how the two moved cards fly between columns without any JS.
 *
 * Timeline (s): 0–0.78 the view builds up (chrome, six cards, the "add task" row) · 1.5–2.45 #4
 * In Progress → Done, turning green as it lands and taking the progress to 2/6 · 2.7–3.62 #2
 * To Do → In Progress. Nothing arrives after the last card lands.
 *
 * SIZING. Everything scales with the widget's own container, and every clamp ceiling is the value
 * its cqw term reaches at 664px — the widest container any preview has (the 768 viewport). Capping
 * lower than that froze the type and padding at ~560px while the square frame kept growing, which
 * is what left a third of the 768 board empty.
 */

@property --tb-n { syntax: '<integer>'; inherits: false; initial-value: 0; }

.mkt-tb {
  container-type: inline-size;
  gap: clamp(7px, 1.7cqw, 11px);
  padding: clamp(11px, 2.8cqw, 17px) clamp(12px, 3.1cqw, 19px) clamp(12px, 3cqw, 18px);

  --tb-gap: clamp(5px, 1.75cqw, 11.6px);   /* between cards in a column */
  --tb-colgap: clamp(5px, 1.35cqw, 8px);   /* between columns */
  --tb-stepx: calc(100% + var(--tb-colgap));
  --tb-stepy: calc(100% + var(--tb-gap));
  --tb-lines: 2;                           /* description lines */
  --tb-title-lines: 2;
  --tb-title-lh: 1.34;                     /* also reserves the title box, see .mkt-tb-title */
  --tb-shadow: 0 1px 2px rgba(20, 22, 43, 0.05);
  --tb-shadow-lift: 0 14px 26px -12px rgba(20, 22, 43, 0.42), 0 2px 6px rgba(20, 22, 43, 0.08);
}

.mkt-tb-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.mkt-tb-num { --tb-n: var(--to); counter-reset: tb var(--tb-n); }
.mkt-tb-num::after { content: counter(tb); }

/* --- head: title + progress --------------------------------------------------- */

.mkt-tb-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.mkt-tb-titles { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.mkt-tb-titles strong {
  font-size: clamp(11px, 2.6cqw, 16.5px);
  font-weight: 600;
  letter-spacing: -0.012em;
  color: #14162B;
  white-space: nowrap;
}
.mkt-tb-titles span {
  font-size: clamp(8px, 1.8cqw, 11.5px);
  line-height: 1.3;
  color: #656B7E;
  white-space: nowrap;
}
/* The progress meter rides in the head row, opposite the screen's name. "Postęp 2/6" is the one
   number the sequence pays off, so it is the only thing the chrome carries. */
.mkt-tb-prog { display: flex; align-items: center; gap: 5px; flex: 0 0 auto; }
.mkt-tb-prog-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: clamp(6.6px, 1.5cqw, 8.4px);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: #7C8196;
}
.mkt-tb-prog-val {
  font-family: 'IBM Plex Mono', monospace;
  font-size: clamp(7.4px, 1.66cqw, 9.4px);
  font-weight: 600;
  color: #14162B;
}
.mkt-tb-prog-bar {
  width: clamp(26px, 7.5cqw, 46px);
  height: 3.5px;
  border-radius: 3px;
  background: #E7E8EF;
  overflow: hidden;
}
.mkt-tb-prog-bar i { display: block; height: 100%; border-radius: 3px; background: #22C55E; transform-origin: 0 50%; transform: scaleX(0.3333); }

/* --- board ------------------------------------------------------------------- */

.mkt-tb-board {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--tb-colgap);
  padding: clamp(6px, 1.7cqw, 10px);
  border: 1px solid #EAEBF2;
  border-radius: 13px;
  background: #F4F5F9;
  position: relative;
  overflow: hidden;
}
/* While a task is on the move the To Do column holds three cards and the third overhangs the board
   floor, so the board — the columns' viewport, which scrolls like the real one — cuts it off. This
   gradient turns that cut into a fade, so the column reads as "continues below" rather than sliced.

   It is only needed while that third card is there. In the settled frame nothing overflows, and a
   fade this tall would wash out the "add task" button sitting just above the board edge — so it
   rests at opacity 0 and is faded in for the transient by the sequence below. That also keeps the
   reduced-motion and no-JS frames, which never hold three cards, completely clear of it. The
   opacity window has to BRACKET the overhang, not sit inside it: measured at 768/1024/1440/1920 the
   third card is out past the board floor from ~0.55s (it enters there) until ~3.20s (the last width
   to close the gap), so tb-edge is opaque from 0.52s to 3.25s.

   The phone preview drops the fade altogether — see the container query below. */
.mkt-tb-board::after {
  content: "";
  position: absolute;
  left: 1px;
  right: 1px;
  bottom: 0;
  z-index: 4;
  height: clamp(26px, 11cqw, 62px);
  background: linear-gradient(to top, #F4F5F9 30%, rgba(244, 245, 249, 0.92) 55%, rgba(244, 245, 249, 0));
  opacity: 0;
  pointer-events: none;
}
.mkt-tb-col {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--tb-gap);
  min-width: 0;
  --tb-drop: 91, 92, 230;
}
.mkt-tb-col.is-done { --tb-drop: 34, 197, 94; }
/* Drop target: the column lights up while a card is on its way in. */
.mkt-tb-col::before {
  content: "";
  position: absolute;
  inset: -4px calc(var(--tb-colgap) * -0.5);
  z-index: 0;
  border-radius: 11px;
  background: rgba(var(--tb-drop), 0.07);
  box-shadow: inset 0 0 0 1.5px rgba(var(--tb-drop), 0.3);
  opacity: 0;
  pointer-events: none;
}

/* The header carries the column name and nothing else: three columns of two cards are counted
   faster than a badge is read. With the counter gone the chip hugs the name instead of keeping the
   column's full width — a white rounded bar 93% empty read as the search field that was deleted,
   not as a header, and at 768 three of them sat across the top of the board. */
.mkt-tb-colh {
  position: relative;
  z-index: 1;
  flex: 0 0 auto;
  align-self: flex-start;
  max-width: 100%;
  display: flex;
  align-items: center;
  padding: clamp(4.5px, 1.15cqw, 6.6px) clamp(6px, 1.5cqw, 9px);
  border: 1px solid #E8E9F0;
  border-radius: 8px;
  background: #FFFFFF;
  box-shadow: var(--tb-shadow);
}
.mkt-tb-colname {
  font-family: 'IBM Plex Mono', monospace;
  font-size: clamp(6.6px, 1.55cqw, 9.6px);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #656B7E;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* --- card -------------------------------------------------------------------- */

/* Never let a column taller than its cards squeeze them: a compressed card slices the last line of
   its title or description. The cards keep their natural height and the slack stays at the bottom
   of the column, the way a real board looks. */
.mkt-tb-card {
  position: relative;
  z-index: 1;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  /* The air the filter row gave back is spent DOWN the card, not across it: the block padding and
     the row gap grow, the inline padding barely moves. Widening the gutters instead had pushed
     "Schema Organization i autorstwo" past its second line at the 1024 preview. */
  gap: clamp(3.5px, 1.32cqw, 7.4px);
  padding: clamp(5.5px, 2.05cqw, 12px) clamp(5.5px, 1.55cqw, 9.3px);
  border: 1px solid #E8E9F0;
  border-radius: 10px;
  background: #FFFFFF;
  box-shadow: var(--tb-shadow);
}
.mkt-tb-card.is-progress { border-color: #D6DAFB; }
.mkt-tb-card.is-done { border-color: #BBF7D0; background: #FCFEFD; }

.mkt-tb-card-h { display: flex; align-items: center; justify-content: space-between; gap: 4px; min-width: 0; }
.mkt-tb-id {
  display: inline-flex;
  align-items: center;
  gap: 0.28em;
  flex: 0 0 auto;
  padding: 0.12em 0.42em;
  border-radius: 5px;
  background: #F1F2F6;
  color: #868CA0;
  font-family: 'IBM Plex Mono', monospace;
  font-size: clamp(6.4px, 1.45cqw, 9.2px);
  font-weight: 600;
}
.mkt-tb-card.is-done .mkt-tb-id { background: #EAFBF0; color: #15803D; }
.mkt-tb-tick {
  position: relative;
  width: 0.85em;
  height: 0.85em;
  border-radius: 50%;
  background: #22C55E;
}
.mkt-tb-tick::after {
  content: "";
  position: absolute;
  left: 0.3em;
  top: 0.16em;
  width: 0.2em;
  height: 0.4em;
  border: solid #FFFFFF;
  border-width: 0 0.11em 0.11em 0;
  transform: rotate(45deg);
}

.mkt-tb-tag {
  display: inline-block;
  max-width: 100%;
  padding: 0.14em 0.5em;
  border: 1px solid;
  border-radius: 999px;
  font-size: clamp(6.4px, 1.5cqw, 9.4px);
  font-weight: 600;
  line-height: 1.45;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mkt-tb-tag.is-cat { color: #4B4CD8; background: #EEF0FE; border-color: #D6DAFB; min-width: 0; }
.mkt-tb-tag.is-prio.is-high { color: #B91C1C; background: #FEF2F2; border-color: #FECACA; }
.mkt-tb-tag.is-prio.is-med { color: #B45309; background: #FFFBEB; border-color: #FDE68A; }
.mkt-tb-tag.is-prio.is-low { color: #656B7E; background: #F6F7F9; border-color: #E3E5EC; }
.mkt-tb-tag.is-diff.is-low { color: #15803D; background: #F2FCF5; border-color: #BBF7D0; }
.mkt-tb-tag.is-diff.is-med { color: #B45309; background: #FFFBEB; border-color: #FDE68A; }
.mkt-tb-tag.is-diff.is-high { color: #B91C1C; background: #FEF2F2; border-color: #FECACA; }
.mkt-tb-tag .is-short { display: none; }

.mkt-tb-title {
  display: -webkit-box;
  -webkit-line-clamp: var(--tb-title-lines);
  -webkit-box-orient: vertical;
  overflow: hidden;
  /* a hair over N line boxes: exactly N clips the descenders of the last line. The reserved height
     and the line height are the same number, so a breakpoint can tighten both at once. */
  min-height: calc(var(--tb-title-lines) * var(--tb-title-lh) * 1em + 1.5px);
  font-size: clamp(8.6px, 2.05cqw, 12.8px);
  font-weight: 600;
  line-height: var(--tb-title-lh);
  color: #14162B;
}
.mkt-tb-meta { display: flex; align-items: center; gap: 4px; min-width: 0; }
.mkt-tb-desc {
  display: -webkit-box;
  -webkit-line-clamp: var(--tb-lines);
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin: 0;
  min-height: calc(var(--tb-lines) * 1.42em + 1.5px);
  font-size: clamp(7.2px, 1.68cqw, 10.4px);
  line-height: 1.42;
  color: #6A7084;
}
.mkt-tb-more {
  padding: clamp(3px, 0.85cqw, 5px);
  border-radius: 7px;
  background: #5B5CE6;
  color: #FFFFFF;
  font-size: clamp(7.4px, 1.75cqw, 11px);
  font-weight: 600;
  line-height: 1.3;
  text-align: center;
}
.mkt-tb-card.is-done .mkt-tb-more { background: #22C55E; }

/* Landing flash on the two cards that move. */
.mkt-tb-ring {
  position: absolute;
  inset: -2px;
  border-radius: 12px;
  box-shadow: 0 0 0 2px rgba(91, 92, 230, 0.55);
  opacity: 0;
  pointer-events: none;
}
.mkt-tb-card.is-done .mkt-tb-ring { box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.6); }

.mkt-tb-add {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  padding: clamp(4px, 1.1cqw, 6.5px);
  border: 1px dashed #C9CCE0;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.55);
  color: #5B5CE6;
  font-size: clamp(7.4px, 1.72cqw, 10.6px);
  font-weight: 600;
  white-space: nowrap;
}
.mkt-tb-add i {
  position: relative;
  width: 1.45em;
  height: 1.45em;
  border-radius: 50%;
  background: #5B5CE6;
  flex: 0 0 auto;
}
.mkt-tb-add i::before,
.mkt-tb-add i::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  background: #FFFFFF;
  border-radius: 1px;
}
.mkt-tb-add i::before { width: 0.62em; height: 1.4px; }
.mkt-tb-add i::after { width: 1.4px; height: 0.62em; }

/* --- widths ------------------------------------------------------------------
 * A container query can only style the container's DESCENDANTS, so the per-width variables are
 * declared on the board and inherit down to the cards. Phone-width previews drop the description
 * and the add row and switch the priority tag to its short label — at 350px the board would
 * otherwise be taller than its frame. The progress meter stays: it is one short line in the head
 * now, and it is the number the sequence exists to move. */

/* Above 560px the padding keeps growing with the container instead of jumping to a fixed value and
 * stopping. Every clamp here is continuous at the 560px boundary (its minimum is the cqw value at
 * 560) and reaches its maximum at 664px, the widest container of the five preview widths — which is
 * what fills the 768 board, where a square frame had outgrown frozen type and padding.
 *
 * Descriptions stay two lines everywhere: they are written to land in two at the narrowest desktop
 * preview (458px), so reserving a third here only opened a blank line inside every card. The card
 * keeps its height through roomier padding and larger type instead. */
@container (min-width: 560px) {
  .mkt-tb-board { padding: clamp(12px, 2.1cqw, 14px); }
  .mkt-tb-card { gap: clamp(8px, 1.78cqw, 11.8px); padding: clamp(12px, 2.62cqw, 17.4px) clamp(12px, 2.1cqw, 14px); }
  .mkt-tb-colh { padding: clamp(6.5px, 1.3cqw, 8.6px) clamp(10px, 1.8cqw, 12px); }
  .mkt-tb-add { padding: clamp(9px, 1.55cqw, 10.3px); }
  .mkt-tb-more { padding: clamp(6px, 1.13cqw, 7.5px); }
  .mkt-tb-prog-label { font-size: clamp(8.4px, 1.4cqw, 9.3px); }
  .mkt-tb-prog-val { font-size: clamp(9.4px, 1.55cqw, 10.4px); }
  .mkt-tb-prog-bar { width: clamp(46px, 7.5cqw, 49.8px); }
}

/* Phone width: ~100px columns, where even a board-length title needs a third line. The description
 * is already gone here, so the room for it is bought back from the card's own padding rather than
 * taken off the title. A container query styles the container's DESCENDANTS, so the line count is
 * set on the board and inherits down, the same way --tb-lines is above. */
@container (max-width: 400px) {
  /* The third line has to be paid for: a tighter title leading, card gap and board padding buy back
     the ~13px it costs, so the bottom card keeps its clearance instead of meeting the board edge. */
  .mkt-tb-board { --tb-title-lines: 3; --tb-title-lh: 1.24; --tb-gap: 6px; padding: 6px; }
  .mkt-tb-card { gap: 3px; padding: 6px 5px; }
  .mkt-tb-more { padding: 3px; }
  .mkt-tb-desc { display: none; }
  .mkt-tb-add { display: none; }
  .mkt-tb-tag .is-full { display: none; }
  .mkt-tb-tag .is-short { display: inline; }
  /* A done card's id chip carries the tick, which makes it ~7px wider than a plain "#2" and left the
     category pill sharing that row 1.4px short — just enough for Chromium to ellipsise "Zawartość
     strony" to "Zawartość s tro…" on the two done cards only. The row gap, the pills' own side
     padding and the card's asymmetric padding above (block 6px, inline 5px) give those pixels back,
     so every category reads in full and the tick stays. */
  .mkt-tb-card-h { gap: 2px; }
  .mkt-tb-tag { padding-inline: 0.36em; }
  /* No overflow fade here: there is no visible overhang to fade. A 249px-tall board leaves the
     third To Do card ~98px BELOW the floor with 0.5px of it showing, while the gradient — 35.6px
     over the full board width — would cover all 15.6px of the "Zobacz więcej" button on the
     settled bottom card of every column, including the two columns that hold nothing below it.
     The wider previews keep it: there 29–49px of that card really does show past the floor. */
  .mkt-tb-board::after { content: none; }
}

/* --- sequence ----------------------------------------------------------------
 * Percentages are absolute times divided by the animation duration, so the four bespoke card
 * timelines stay in step with each other and with the progress meter. */

/* The chrome — the title, the progress meter, the board frame, the three column headers and the
   "add task" row — is in place by ~0.78s, so the preview never sits on an empty grey board and the
   sequence can end on the last task landing rather than on a control arriving. The four moved cards
   hold their start state inside their own fly/slide keyframes; those hold points are the same
   absolute times as the plain cards' delays below (slot --i), so they move together:
   i=0 0.42s · i=1 0.48s · i=2 0.54s · i=3 0.60s · i=4 0.66s · i=5 0.72s. */
.mkt-tb.is-armed .mkt-tb-head { animation: tb-rise 0.45s cubic-bezier(0.22, 1, 0.36, 1) backwards; }
.mkt-tb.is-armed .mkt-tb-prog { animation: tb-fade 0.4s ease 0.18s backwards; }
.mkt-tb.is-armed .mkt-tb-board { animation: tb-fade 0.5s ease 0.1s backwards; }
.mkt-tb.is-armed .mkt-tb-colh { animation: tb-rise 0.45s cubic-bezier(0.22, 1, 0.36, 1) calc(0.2s + var(--c) * 0.06s) backwards; }
/* The overflow fade: opaque from 0.52s (just before the third To Do card lands at 0.54s) to 3.25s
   (just after the last width closes the gap), then out by 3.6s. It rests at opacity 0, so the
   settled frame — and the no-JS and reduced-motion frames, which ARE the settled frame — never
   carry it, and the phone container removes the pseudo-element outright. */
.mkt-tb.is-armed .mkt-tb-board::after { animation: tb-edge 3.6s linear backwards; }
.mkt-tb.is-armed .mkt-tb-card { animation: tb-in 0.5s cubic-bezier(0.2, 0.9, 0.3, 1.2) calc(0.42s + var(--i) * 0.06s) backwards; }
.mkt-tb.is-armed .mkt-tb-add { animation: tb-pop 0.45s cubic-bezier(0.2, 0.9, 0.3, 1.3) 0.78s backwards; }

/* #4: In Progress slot 1 → Done slot 2 (one column right, one row down). Its markup is a finished
   task, so everything green about it — the card, the id, the tick, the button — is held back until
   it lands. */
.mkt-tb.is-armed .mkt-tb-card.is-fly-a { animation: tb-fly-a 2.45s linear backwards, tb-card-open 2.6s linear backwards; z-index: 3; }
.mkt-tb.is-armed .mkt-tb-card.is-fly-a .mkt-tb-id { animation: tb-id-open 2.6s linear backwards; }
.mkt-tb.is-armed .mkt-tb-card.is-fly-a .mkt-tb-tick { animation: tb-tick 2.6s linear backwards; }
.mkt-tb.is-armed .mkt-tb-card.is-fly-a .mkt-tb-more { animation: tb-more-open 2.6s linear backwards; }
.mkt-tb.is-armed .mkt-tb-card.is-fly-a .mkt-tb-ring { animation: tb-ring-a 3.15s linear backwards; }
.mkt-tb.is-armed .mkt-tb-col.is-done::before { animation: tb-drop-a 2.6s linear backwards; }

/* #2: To Do slot 2 → In Progress slot 2 (one column right, same row), plain until it lands. */
.mkt-tb.is-armed .mkt-tb-card.is-fly-b { animation: tb-fly-b 3.62s linear backwards, tb-card-open-b 3.75s linear backwards; z-index: 3; }
.mkt-tb.is-armed .mkt-tb-card.is-fly-b .mkt-tb-ring { animation: tb-ring-b 4.3s linear backwards; }
.mkt-tb.is-armed .mkt-tb-col.is-progress::before { animation: tb-drop-b 3.75s linear backwards; }

/* The cards left behind close the gap. */
.mkt-tb.is-armed .mkt-tb-card.is-slide-a { animation: tb-slide-a 2.3s linear backwards; }
.mkt-tb.is-armed .mkt-tb-card.is-slide-b { animation: tb-slide-b 3.5s linear backwards; }

/* The progress reads 1/6 until #4 lands in Done at 2.30s, then 2/6 with the bar running after it. */
.mkt-tb.is-armed .mkt-tb-num.is-prog { animation: tb-c-done 2.3s step-end backwards; }
.mkt-tb.is-armed .mkt-tb-prog-bar i { animation: tb-bar 0.55s cubic-bezier(0.22, 1, 0.36, 1) 2.3s backwards; }

/* Waiting for marketing.js to mark the widget live: hold the first frame. */
.mkt-widget:not(.is-live) .mkt-tb.is-armed *,
.mkt-widget:not(.is-live) .mkt-tb.is-armed *::before,
.mkt-widget:not(.is-live) .mkt-tb.is-armed *::after { animation-play-state: paused !important; }

@media (prefers-reduced-motion: reduce) {
  .mkt-tb *,
  .mkt-tb *::before,
  .mkt-tb *::after { animation: none !important; }
}

@keyframes tb-fade { from { opacity: 0; } }
@keyframes tb-rise { from { opacity: 0; translate: 0 5px; } }
@keyframes tb-pop { from { opacity: 0; scale: 0.88; } }
@keyframes tb-in { from { opacity: 0; scale: 0.94; translate: 0 6px; } }

@keyframes tb-fly-a {
  0%, 24.5% { opacity: 0; scale: 0.94; translate: calc(-1 * var(--tb-stepx)) calc(-1 * var(--tb-stepy)); animation-timing-function: cubic-bezier(0.2, 0.9, 0.3, 1.2); }
  53.5% { opacity: 1; scale: 1; rotate: 0deg; translate: calc(-1 * var(--tb-stepx)) calc(-1 * var(--tb-stepy)); box-shadow: var(--tb-shadow); }
  61.2% { opacity: 1; scale: 1; rotate: 0deg; translate: calc(-1 * var(--tb-stepx)) calc(-1 * var(--tb-stepy)); box-shadow: var(--tb-shadow); animation-timing-function: cubic-bezier(0.3, 0.8, 0.4, 1.1); }
  69.4% { scale: 1.035; rotate: -1.4deg; translate: calc(-1 * var(--tb-stepx)) calc(-1 * var(--tb-stepy)); box-shadow: var(--tb-shadow-lift); animation-timing-function: cubic-bezier(0.5, 0, 0.2, 1); }
  93.1% { scale: 1.035; rotate: -1.4deg; translate: 0 0; box-shadow: var(--tb-shadow-lift); animation-timing-function: cubic-bezier(0.34, 1.35, 0.64, 1); }
  100% { scale: 1; rotate: 0deg; translate: 0 0; }
}

@keyframes tb-fly-b {
  0%, 13.3% { opacity: 0; scale: 0.94; translate: calc(-1 * var(--tb-stepx)) 0; animation-timing-function: cubic-bezier(0.2, 0.9, 0.3, 1.2); }
  32.3% { opacity: 1; scale: 1; rotate: 0deg; translate: calc(-1 * var(--tb-stepx)) 0; box-shadow: var(--tb-shadow); }
  74.6% { opacity: 1; scale: 1; rotate: 0deg; translate: calc(-1 * var(--tb-stepx)) 0; box-shadow: var(--tb-shadow); animation-timing-function: cubic-bezier(0.3, 0.8, 0.4, 1.1); }
  80.1% { scale: 1.035; rotate: 1.4deg; translate: calc(-1 * var(--tb-stepx)) 0; box-shadow: var(--tb-shadow-lift); animation-timing-function: cubic-bezier(0.5, 0, 0.2, 1); }
  95.6% { scale: 1.035; rotate: 1.4deg; translate: 0 0; box-shadow: var(--tb-shadow-lift); animation-timing-function: cubic-bezier(0.34, 1.35, 0.64, 1); }
  100% { scale: 1; rotate: 0deg; translate: 0 0; }
}

@keyframes tb-slide-a {
  0%, 28.7% { opacity: 0; scale: 0.94; translate: 0 var(--tb-stepy); animation-timing-function: cubic-bezier(0.2, 0.9, 0.3, 1.2); }
  60% { opacity: 1; scale: 1; translate: 0 var(--tb-stepy); }
  80.4% { translate: 0 var(--tb-stepy); animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
  100% { translate: 0 0; }
}

@keyframes tb-slide-b {
  0%, 15.4% { opacity: 0; scale: 0.94; translate: 0 var(--tb-stepy); animation-timing-function: cubic-bezier(0.2, 0.9, 0.3, 1.2); }
  35.4% { opacity: 1; scale: 1; translate: 0 var(--tb-stepy); }
  87.1% { translate: 0 var(--tb-stepy); animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
  100% { translate: 0 0; }
}

/* The moved card only becomes a "done" card once it lands (2.30s of 2.60s = 88.5%). */
@keyframes tb-card-open { 0%, 88.5% { border-color: #D6DAFB; background: #FFFFFF; } }
@keyframes tb-card-open-b { 0%, 92% { border-color: #E8E9F0; } }
@keyframes tb-id-open { 0%, 88.5% { background: #F1F2F6; color: #868CA0; } }
@keyframes tb-more-open { 0%, 88.5% { background: #5B5CE6; } }
@keyframes tb-tick { 0%, 88.5% { opacity: 0; scale: 0.4; } 100% { opacity: 1; scale: 1; } }

@keyframes tb-drop-a { 0%, 61.5% { opacity: 0; } 69%, 92% { opacity: 1; } 100% { opacity: 0; } }
@keyframes tb-drop-b { 0%, 74.1% { opacity: 0; } 78.7%, 95.5% { opacity: 1; } 100% { opacity: 0; } }
@keyframes tb-ring-a { 0%, 77.8% { opacity: 0; scale: 1; } 80.6% { opacity: 0.85; } 100% { opacity: 0; scale: 1.04; } }
@keyframes tb-ring-b { 0%, 84.2% { opacity: 0; scale: 1; } 86.5% { opacity: 0.8; } 100% { opacity: 0; scale: 1.04; } }

/* 3.6s: opaque 0.52s (14.4%) → 3.25s (90.3%). Measured at 768/1024/1440/1920, the third To Do card
   is out past the board floor from 0.56s to 3.20s, so that window brackets it at every size that
   draws the fade. */
@keyframes tb-edge { 0%, 11.1% { opacity: 0; } 14.4%, 90.3% { opacity: 1; } 100% { opacity: 0; } }
@keyframes tb-c-done { from { --tb-n: 1; } }
@keyframes tb-bar { from { transform: scaleX(0.1667); } }

/* === task-detail =========================================================== */
/*
 * Szczegóły zadania — /produkt-claude-preview, step 3 "Działaj" (widgets/v2/task-detail.blade.php).
 *
 * The modal the Task Manager opens on a card: the task with its three pills, a row of four facts,
 * the long-form detail on the left and the BIA thread on the right. The story is one conversation:
 * BIA offers three things it can tighten, the user asks about the first ("Układ tabeli"), BIA
 * answers, and the answer lands in the task description — the lilac block on the left and the
 * "Zaktualizowano" badge next to it.
 *
 * The left pane is the task explained and nothing else: what to do, why it matters, which pages it
 * touches, how to tell it is done — the same four things the app's own task object carries. Being
 * done is not a form at the bottom of that pane; it is one small control in the "Status" cell, the
 * unticked box the recommended list also uses (.mkt-rt-add), and the checklist is drawn in the
 * same box so the two read as one idea.
 *
 * Every rule below describes the finished, refined task; the sequence that leads to it is a set of
 * keyframe animations whose "from" frames hold the earlier states. They apply only once the widget
 * is armed by its inline script, sit paused until marketing.js marks the widget live, and restart
 * by themselves each time the accordion shows the widget again (display: none cancels CSS
 * animations).
 *
 * Timeline (s): 0–0.45 the task header · 0.15–1.15 the facts, the rings and the +2 prediction ·
 * 0.48–1.1 the detail, the pages and the checklist · 0.4–1.1 the BIA panel, the pinned task and
 * the three suggestions · 1.12–1.95 the question is picked and typed · 2.15–2.85 BIA thinks ·
 * 2.85 the answer · 3.25–3.8 it lands in the description · 3.7–5.9 the thread's "done" receipt
 * opens, is read and clears itself · 4.0 the status control is offered, and clears itself too —
 * what is left is the settled frame, the one that also shows with no JS and under reduced motion.
 *
 * Four preview sizes share this file (body container widths): ~662 px (one-column step layout),
 * ~568 (desktop), ~481 (1024 viewport) and ~328 (phone). The two narrow tiers drop sections rather
 * than shrink everything into illegibility — the task, its facts and the exchange that changes it
 * always survive. At ~481 only BIA's suggestions go; at ~328 the task, its facts, its pages and the
 * question-and-answer that change it remain.
 *
 * Both columns are one grid row, so neither may set its own height: the pane shares out its spare
 * height between sections (space-between) and the thread hangs off the composer (flex-end) the way
 * a real chat panel does. The paragraphs and BIA's refinement are line-clamped so that copy longer
 * than the Polish original — the English set is ~35 px taller — cannot spend more room than the
 * frame has.
 */

@property --tdet-n { syntax: '<integer>'; inherits: false; initial-value: 0; }
/* The thread's vertical rhythm, as a multiplier on the per-tier --td-gap-b. It rests at 1 — the
   settled frame, and equally the no-JS and reduced-motion one — and the sequence holds it at half
   while the conversation is still arriving and the "done" receipt is on screen, because the thread
   is exactly full then. When the receipt clears, the gaps take the room it gives back instead of
   letting it pool under the panel header. */
@property --td-gap-k { syntax: '<number>'; inherits: true; initial-value: 1; }

/* Two query containers, because an element cannot answer its own container query: the widget frame
   sizes the body's own padding and gap, the body sizes everything inside it. The frame is the body
   plus that padding, so the two scales differ by ~30 px — far from every breakpoint at the sizes
   this page actually renders (348 / 507 / 598 / 702 px frames). */
.mkt-widget:has(.mkt-td) { container-type: inline-size; }

.mkt-widget-body.mkt-td {
  gap: 8px;
  padding: 14px 15px;
  container-type: inline-size;
  background: #FFFFFF;
}

.mkt-td-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.mkt-td-num { --tdet-n: var(--to); counter-reset: tdet var(--tdet-n); }
.mkt-td-num::after { content: counter(tdet); }

/* --- task header ------------------------------------------------------------- */

.mkt-td-head { display: flex; flex-direction: column; gap: 6px; }

.mkt-td-title {
  display: block;
  font-size: 14px;
  line-height: 1.34;
  font-weight: 650;
  letter-spacing: -0.012em;
  color: #14162B;
  text-wrap: pretty;
}
.mkt-td-hash {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  font-weight: 600;
  color: #9095AB;
  margin-right: 6px;
}

/* The same three tags the Task Manager card carries, so the board and this modal agree. */
.mkt-td-pills { display: flex; flex-wrap: wrap; gap: 5px; }
.mkt-td-pill {
  font-size: 10px;
  font-weight: 500;
  line-height: 1.5;
  padding: 2.5px 8px;
  border: 1px solid;
  border-radius: 999px;
  white-space: nowrap;
}
.mkt-td-pill.is-cat { color: #4B4CD8; border-color: #D6DAFB; background: #EEF0FE; }
.mkt-td-pill.is-prio { color: #B91C1C; border-color: #FECACA; background: #FEF2F2; }
.mkt-td-pill.is-diff { color: #B45309; border-color: #FDE68A; background: #FFFBEB; }

/* --- the four facts ----------------------------------------------------------- */

.mkt-td-facts {
  margin: 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  border: 1px solid #EDEEF2;
  border-radius: 12px;
  background: #FCFCFE;
  overflow: hidden;
}
.mkt-td-fact {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px 10px;
  min-width: 0;
}
.mkt-td-fact + .mkt-td-fact { border-left: 1px solid #EDEEF2; }
.mkt-td-fact dt {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8.8px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #7A7F93;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mkt-td-fact dd {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 30px;
  min-width: 0;
}
.mkt-td-fact-strong {
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.3;
  color: #14162B;
}
/* The task's whole completion control: an unticked box and the state it names. A task is tracked
   the way a tracker tracks it — no comment field, no full-width "mark as complete" form. The box is
   the recommended list's add control at this tier's scale (.mkt-rt-add: white, 1px lilac inset,
   green with a white tick once it flips), so "not done yet" looks the same in both widgets. */
.mkt-td-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #4C5163;
}
.mkt-td-box {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  border-radius: 4px;
  background: #FFFFFF;
  box-shadow: inset 0 0 0 1px #D6DAFB;
}

.mkt-td-ring { position: relative; width: 32px; height: 32px; flex: 0 0 auto; }
.mkt-td-ring svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
.mkt-td-ring circle { fill: none; stroke-width: 4.5; }
.mkt-td-ring .is-track { stroke: #EDEEF2; }
.mkt-td-ring .is-value {
  stroke: #5B5CE6;
  stroke-linecap: round;
  stroke-dasharray: 100;
  stroke-dashoffset: calc(100 - var(--score));
}
.mkt-td-ring.is-up .is-value { stroke: #22C55E; }
.mkt-td-ring .mkt-td-num {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11.5px;
  font-weight: 650;
  color: #14162B;
}

.mkt-td-delta {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 10px;
  font-weight: 650;
  color: #15803D;
  background: #E9F7EF;
  border-radius: 999px;
  padding: 2.5px 6px;
  white-space: nowrap;
}
.mkt-td-delta svg { width: 8px; height: 8px; fill: #22C55E; flex: 0 0 auto; }
.mkt-td-delta .is-short { display: none; }

/* --- detail + conversation ---------------------------------------------------- */

/* The detail carries more lines than the conversation, so it takes the wider half: the extra width
   keeps the pane's paragraphs off their clamps, and the narrower chat wraps its bubbles the way a
   side panel really does — which is also what fills the thread instead of leaving a band of air. */
.mkt-td-split {
  display: grid;
  grid-template-columns: 1fr 0.85fr;
  gap: 10px;
  flex: 1;
  min-height: 0;
}
/* The pane and the chat are one grid row, so both are exactly as tall as the split — the pane can
   neither push past it (the violet button would bleed out of the frame) nor leave a hole above the
   footer. space-between spends whatever the copy leaves over on the gaps between sections, so the
   column always fills; the line clamps below stop longer copy (EN) from spending more than there is. */
.mkt-td-pane {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 6px;
  min-width: 0;
  min-height: 0;
}

.mkt-td-kicker {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #7A7F93;
}
.mkt-td-upd {
  display: inline-flex;
  align-items: center;
  font-size: 8.2px;
  letter-spacing: 0.06em;
  color: #15803D;
  background: #E9F7EF;
  border-radius: 999px;
  padding: 2px 6px;
}
/* Three lines, not two: the room the comment field and the completion button gave back goes to the
   copy that explains the task, so the longer English wording finishes its sentence. */
.mkt-td-p {
  margin: 3px 0 0;
  font-size: 11.5px;
  line-height: 1.45;
  color: #4C5163;
  text-wrap: pretty;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.mkt-td-refine {
  border: 1px solid #DCDDFB;
  border-radius: 9px;
  background: #F4F5FF;
  padding: 5px 9px;
  font-size: 11px;
  line-height: 1.4;
  color: #3F40BE;
}
.mkt-td-refine-label {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-bottom: 3px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8.2px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #6D6EE0;
}
/* Bounded like the paragraphs: BIA's refinement is the longest string in the pane. */
.mkt-td-refine-text {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* Both of these carry a phone-length half as well, the way the prediction delta and BIA's answer
   already do: at ~328 px the full sentences run past the clamp and end in an ellipsis with slack
   under them, which reads as a layout that gave up rather than as air. Everywhere else the full
   sentence is what shows, and the clamps above are only the guard rail. */
.mkt-td-p .is-short,
.mkt-td-refine-text .is-short { display: none; }

.mkt-td-list { margin: 4px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 3px; }
.mkt-td-list li {
  position: relative;
  padding-left: 13px;
  font-size: 11px;
  line-height: 1.45;
  color: #4C5163;
}
.mkt-td-list li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 6px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: #C1C2F3;
}

/* "Dotyczy podstron" — the pages the task touches (BrandAuditTask.sample_pages in the app), the
   one thing the pane was missing. One row, kicker and chips together, exactly as the recommended
   list draws it (.mkt-rt-affected), with the same two paths, so the two widgets say the same thing
   the same way. */
.mkt-td-sec.is-pages { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
.mkt-td-pane .mkt-path-chip { font-size: 10px; padding: 2px 7px; }

/* "Jak sprawdzić, że zrobione?" — the task is still TO DO, so the boxes are empty: they are what
   the status control at the top of the modal ticks off, and they are drawn like it. */
.mkt-td-check { margin: 4px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 4px; }
.mkt-td-check li {
  display: flex;
  align-items: flex-start;
  gap: 7px;
  font-size: 11px;
  line-height: 1.45;
  color: #4C5163;
}
.mkt-td-check li i {
  flex: 0 0 auto;
  width: 11px;
  height: 11px;
  margin-top: 2px;
  border-radius: 3.5px;
  background: #FFFFFF;
  box-shadow: inset 0 0 0 1px #D6DAFB;
}

.mkt-td-chat {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
  min-height: 0;
  border: 1px solid #EDEEF2;
  border-radius: 12px;
  background: #FAFAFC;
  padding: 10px;
}
.mkt-td-chat-h {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 11.5px;
  font-weight: 650;
  color: #14162B;
}
.mkt-td-online { width: 6px; height: 6px; border-radius: 999px; background: #22C55E; flex: 0 0 auto; }

/* The thread reads top-down like the assistant's own panel; the composer holds the bottom. Spacing
   is margin, not gap, so a collapsed slot takes no room at all. */
.mkt-td-thread {
  --td-gap-b: 24px;
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  overflow: hidden;
}
/* The task this conversation is pinned to — the app's @mention, in the marketing page's language. */
.mkt-td-ctx {
  display: flex;
  align-items: center;
  gap: 7px;
}
.mkt-td-ctx::before,
.mkt-td-ctx::after { content: ""; flex: 1; height: 1px; background: #E9EAF0; }
.mkt-td-ctx-chip {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8.4px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #6D6EE0;
  background: #FFFFFF;
  border: 1px solid #DCDDFB;
  border-radius: 999px;
  padding: 2px 7px;
  white-space: nowrap;
}

.mkt-td-msg {
  position: relative;
  font-size: 11.5px;
  line-height: 1.45;
  padding: 7px 9px;
  border-radius: 11px;
  max-width: 94%;
  text-wrap: pretty;
}
.mkt-td-msg.is-bot {
  align-self: flex-start;
  border-bottom-left-radius: 4px;
  background: #FFFFFF;
  border: 1px solid #E9EAF0;
  color: #262A47;
}
.mkt-td-msg.is-user {
  align-self: flex-end;
  max-width: 90%;
  border-bottom-right-radius: 4px;
  background: #14162B;
  color: #FFFFFF;
}
.mkt-td-msg .is-short { display: none; }
.mkt-td-thread > .mkt-td-msg { margin-top: calc(var(--td-gap-b) * var(--td-gap-k)); }

/* What BIA can tighten in this task. The first one is the thread the user pulls: it turns violet
   as the question is typed. */
.mkt-td-chips { margin-top: calc(var(--td-gap-b) * var(--td-gap-k)); display: flex; flex-direction: column; gap: 5px; }
.mkt-td-chips-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8.2px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #9095AB;
}
.mkt-td-chip-row { display: flex; flex-wrap: wrap; gap: 4px; }
.mkt-td-chip {
  font-size: 9.8px;
  line-height: 1.5;
  color: #4C5163;
  background: #FFFFFF;
  border: 1px solid #E3E5EC;
  border-radius: 999px;
  padding: 3px 8px;
  white-space: nowrap;
}
.mkt-td-chip.is-picked { color: #4B4CD8; border-color: #C9CAF7; background: #EEF0FE; font-weight: 500; }

/* Thread items arrive one by one: the row they sit in grows from 0fr, so the conversation grows
   downwards the way a real thread does. */
.mkt-td-slot { display: grid; grid-template-rows: 1fr; min-height: 0; }
.mkt-td-slot > .mkt-td-slot-in {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
  padding-top: 7px;
}
/* The thinking line and the "done" note both leave nothing behind once they collapse: a 0fr row
   cannot squeeze padding out of its item (border-box floors at the padding), so their spacing
   lives on the line itself. */
.mkt-td-slot.is-think { grid-template-rows: 0fr; }
/* The "done" note is a receipt, not a state: it lands, is read, and goes again, so its settled
   state — the end of the sequence, and equally the no-JS and reduced-motion frame — is a closed
   row with nothing in it. The refined description and its "Zaktualizowano" badge are what the
   frame keeps. The siblings polished in the same round clear their "Strategia zostala zapisana" /
   "Propozycje gotowe" line exactly this way. */
.mkt-td-slot.is-note { grid-template-rows: 0fr; }
/* Bubbles sit further apart than the pane's single slot — chat spacing, and it is what spends the
   conversation's spare height instead of parking it under the panel header. --td-gap-b is the
   settled pitch, measured so the finished thread leaves ~10 px under the header at every tier in
   both languages; --td-gap-k halves it for as long as the "done" receipt is on screen, because the
   receipt needs that room and the thread has no more to give. */
.mkt-td-thread .mkt-td-slot > .mkt-td-slot-in { padding-top: calc(var(--td-gap-b) * var(--td-gap-k)); }
.mkt-td-thread .mkt-td-slot.is-think > .mkt-td-slot-in,
.mkt-td-thread .mkt-td-slot.is-note > .mkt-td-slot-in { padding-top: 0; }

.mkt-td-think {
  margin-top: calc(var(--td-gap-b) * var(--td-gap-k));
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1px;
  padding: 2px 0;
  font-size: 10.5px;
  color: #7A7F93;
}
.mkt-td-dots { display: inline-flex; }
.mkt-td-dots span { opacity: 0.35; }

.mkt-td-note {
  margin-top: calc(var(--td-gap-b) * var(--td-gap-k));
  position: relative;
  display: flex;
  align-items: center;
  opacity: 0;
  gap: 5px;
  border-radius: 8px;
  background: #EEFAF3;
  color: #15803D;
  font-size: 9.8px;
  font-weight: 600;
  padding: 5px 8px;
}
.mkt-td-note svg {
  width: 10px;
  height: 10px;
  flex: 0 0 auto;
  fill: none;
  stroke: #22C55E;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.mkt-td-compose {
  display: flex;
  align-items: center;
  gap: 8px;
  border-top: 1px solid #E9EAF0;
  padding-top: 8px;
}
.mkt-td-field { position: relative; flex: 1; min-width: 0; height: 16px; }
.mkt-td-ph,
.mkt-td-typed {
  position: absolute;
  left: 0;
  top: 0;
  font-size: 11px;
  line-height: 16px;
  white-space: nowrap;
}
.mkt-td-ph { color: #9095AB; }
.mkt-td-typed {
  width: max-content;
  max-width: 0;
  overflow: hidden;
  opacity: 0;
  color: #14162B;
  font-weight: 500;
  border-right: 1.5px solid transparent;
}
.mkt-td-send {
  width: 24px;
  height: 24px;
  flex: 0 0 auto;
  border-radius: 8px;
  background: #C4C8D6;
  display: flex;
  align-items: center;
  justify-content: center;
}
.mkt-td-send svg { width: 11px; height: 11px; display: block; }

/* --- sequence ------------------------------------------------------------------ */

.mkt-td.is-armed .mkt-td-title { animation: td-rise 0.45s cubic-bezier(0.22, 1, 0.36, 1) both; }
.mkt-td.is-armed .mkt-td-pill { animation: td-pop 0.36s cubic-bezier(0.2, 0.9, 0.3, 1.25) calc(0.14s + var(--i) * 0.07s) both; }
.mkt-td.is-armed .mkt-td-facts { animation: td-fade 0.36s ease 0.15s both; }
.mkt-td.is-armed .mkt-td-fact { animation: td-pop 0.4s cubic-bezier(0.2, 0.9, 0.3, 1.2) calc(0.18s + var(--i) * 0.075s) both; }
.mkt-td.is-armed .mkt-td-ring .is-value { animation: td-ring 0.8s cubic-bezier(0.22, 1, 0.36, 1) calc(0.33s + (var(--i) - 2) * 0.18s) both; }
.mkt-td.is-armed .mkt-td-ring .mkt-td-num { animation: td-count 0.8s cubic-bezier(0.22, 1, 0.36, 1) calc(0.33s + (var(--i) - 2) * 0.18s) both; }
.mkt-td.is-armed .mkt-td-delta { animation: td-pop 0.36s cubic-bezier(0.2, 0.9, 0.3, 1.35) 0.98s both; }

.mkt-td.is-armed .mkt-td-sec { animation: td-rise 0.42s cubic-bezier(0.22, 1, 0.36, 1) calc(0.48s + var(--i) * 0.09s) both; }
/* The paths land on the beat the old "na co wpływa" bullets had, in the pills' own idiom. */
.mkt-td.is-armed .mkt-td-list li { animation: td-rise 0.38s cubic-bezier(0.22, 1, 0.36, 1) calc(0.78s + var(--i) * 0.06s) both; }
.mkt-td.is-armed .mkt-td-pane .mkt-path-chip { animation: td-pop 0.34s cubic-bezier(0.2, 0.9, 0.3, 1.25) calc(0.9s + var(--i) * 0.07s) both; }
.mkt-td.is-armed .mkt-td-check li { animation: td-rise 0.38s cubic-bezier(0.22, 1, 0.36, 1) calc(0.88s + var(--i) * 0.07s) both; }

.mkt-td.is-armed .mkt-td-chat { animation: td-fade 0.42s ease 0.4s both; }
.mkt-td.is-armed .mkt-td-ctx { animation: td-fade 0.38s ease 0.58s both; }
.mkt-td.is-armed .mkt-td-thread > .mkt-td-msg { animation: td-rise 0.42s cubic-bezier(0.22, 1, 0.36, 1) 0.7s both; }
.mkt-td.is-armed .mkt-td-chips-label { animation: td-fade 0.36s ease 0.86s both; }
.mkt-td.is-armed .mkt-td-chip { animation: td-pop 0.34s cubic-bezier(0.2, 0.9, 0.3, 1.25) calc(0.9s + var(--i) * 0.06s) both; }
.mkt-td.is-armed .mkt-td-compose { animation: td-fade 0.38s ease 0.95s both; }

/* The first suggestion is picked, the question is typed into the composer and sent: the send key
   lights up, the line clears and the same words arrive in the thread. */
.mkt-td.is-armed .mkt-td-chip.is-picked { animation: td-pick 0.38s ease 1.12s backwards; }
.mkt-td.is-armed .mkt-td-typed { animation: td-type 0.8s steps(22, end) 1.15s both; }
.mkt-td.is-armed .mkt-td-ph { animation: td-ph 0.8s steps(1, end) 1.15s both; }
.mkt-td.is-armed .mkt-td-send { animation: td-send 0.8s steps(1, end) 1.15s both; }

.mkt-td.is-armed .mkt-td-slot.is-ask { animation: td-open 0.36s cubic-bezier(0.22, 1, 0.36, 1) 1.92s backwards; }
.mkt-td.is-armed .mkt-td-slot.is-ask .mkt-td-msg { animation: td-rise 0.38s cubic-bezier(0.22, 1, 0.36, 1) 1.96s both; }

.mkt-td.is-armed .mkt-td-slot.is-think { animation: td-think 0.74s linear 2.15s both; }
.mkt-td.is-armed .mkt-td-think-label { animation: td-dim 0.9s ease-in-out 2.19s 1 both; }
.mkt-td.is-armed .mkt-td-dots span { animation: td-dim 0.45s ease-in-out 2.19s 2 both; }
.mkt-td.is-armed .mkt-td-dots span:nth-child(2) { animation-delay: 2.29s; }
.mkt-td.is-armed .mkt-td-dots span:nth-child(3) { animation-delay: 2.39s; }

/* BIA's mark equalizes while it works, then settles — the same loading mark the assistant uses
   everywhere else on the site, held to this one window. */
.mkt-td .mkt-td-chat-h .mkt-bia-icon.is-loading .mkt-bia-bar { animation: none; }
.mkt-td .mkt-td-chat-h .mkt-bia-icon.is-loading .mkt-bia-node { animation: none; opacity: 0; }
.mkt-td.is-armed .mkt-td-chat-h .mkt-bia-icon .mkt-bia-bar.is-s { animation: td-eq 0.7s ease-in-out 2.15s 1 both; }
.mkt-td.is-armed .mkt-td-chat-h .mkt-bia-icon .mkt-bia-bar.is-m { animation: td-eq 0.7s ease-in-out 2.23s 1 both; }
.mkt-td.is-armed .mkt-td-chat-h .mkt-bia-icon .mkt-bia-bar.is-a { animation: td-eq 0.7s ease-in-out 2.31s 1 both; }
.mkt-td.is-armed .mkt-td-chat-h .mkt-bia-icon .mkt-bia-node.is-a { animation: td-node 0.66s ease-in-out 2.17s 1 both; }
.mkt-td.is-armed .mkt-td-chat-h .mkt-bia-icon .mkt-bia-node.is-b { animation: td-node 0.66s ease-in-out 2.29s 1 both; }
.mkt-td.is-armed .mkt-td-chat-h .mkt-bia-icon .mkt-bia-node.is-c { animation: td-node 0.66s ease-in-out 2.41s 1 both; }

.mkt-td.is-armed .mkt-td-slot.is-answer { animation: td-open 0.4s cubic-bezier(0.22, 1, 0.36, 1) 2.85s backwards; }
.mkt-td.is-armed .mkt-td-slot.is-answer .mkt-td-msg { animation: td-rise 0.42s cubic-bezier(0.22, 1, 0.36, 1) 2.91s both; }

/* The answer lands in the task description. */
.mkt-td.is-armed .mkt-td-slot.is-refine { animation: td-open 0.42s cubic-bezier(0.22, 1, 0.36, 1) 3.25s backwards; }
.mkt-td.is-armed .mkt-td-refine { animation: td-land 0.9s ease 3.3s both; }
.mkt-td.is-armed .mkt-td-upd { animation: td-pop 0.36s cubic-bezier(0.2, 0.9, 0.3, 1.35) 3.45s backwards; }

/* The receipt has a life, not a landing: the row opens at 3.70s, the note is legible from 4.12s
   for 1.12s — the dwell the sibling widgets give their own "done" line — and then both clear, so
   the thread ends on the exchange and the payoff stays on the left. Both elements rest at "gone",
   so these keyframes spell their visible state out instead of leaning on the base rules. */
.mkt-td.is-armed .mkt-td-slot.is-note { animation: td-note-row 2.2s linear 3.7s both; }
.mkt-td.is-armed .mkt-td-note { animation: td-note-life 2.16s linear 3.74s both; }

/* The thread relaxes as the receipt goes: the gaps take back exactly the height the note had, so
   the conversation stays flush with the panel from the first bubble to the last frame — no band
   under the header at the end, and no bubble pushed out of the bottom while the note is up. */
.mkt-td.is-armed .mkt-td-thread { animation: td-gap 0.6s ease 5.3s both; }

/* Last beat: the task is clear enough to act on, so the eye is sent back to the one control that
   closes it. The ring blooms and clears in place — it leaves the settled frame exactly as it found
   it, which is also the frame that shows with no JS and under reduced motion. */
.mkt-td.is-armed .mkt-td-box { animation: td-ready 0.7s ease 4s both; }

/* Waiting for marketing.js to mark the widget live: hold the first frame. */
.mkt-widget:not(.is-live) .mkt-td.is-armed,
.mkt-widget:not(.is-live) .mkt-td.is-armed *,
.mkt-widget:not(.is-live) .mkt-td.is-armed *::after,
.mkt-widget:not(.is-live) .mkt-td.is-armed *::before { animation-play-state: paused !important; }

@media (prefers-reduced-motion: reduce) {
  .mkt-td,
  .mkt-td *,
  .mkt-td *::after,
  .mkt-td *::before { animation: none !important; }
}

@keyframes td-rise { from { opacity: 0; translate: 0 7px; } }
@keyframes td-pop { from { opacity: 0; scale: 0.88; } }
@keyframes td-fade { from { opacity: 0; } }
@keyframes td-open { from { grid-template-rows: 0fr; } }
@keyframes td-ring { from { stroke-dashoffset: 100; } }
@keyframes td-count { from { --tdet-n: var(--from, 0); } }
@keyframes td-pick { from { color: #4C5163; border-color: #E3E5EC; background: #FFFFFF; } }
@keyframes td-think {
  0% { grid-template-rows: 0fr; }
  18%, 84% { grid-template-rows: 1fr; }
  100% { grid-template-rows: 0fr; }
}
@keyframes td-gap { from { --td-gap-k: 0.5; } }
@keyframes td-note-row {
  0% { grid-template-rows: 0fr; animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
  16.4%, 72.7% { grid-template-rows: 1fr; animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1); }
  100% { grid-template-rows: 0fr; }
}
@keyframes td-note-life {
  0% { opacity: 0; scale: 0.88; animation-timing-function: cubic-bezier(0.2, 0.9, 0.3, 1.25); }
  17.6%, 69.4% { opacity: 1; scale: 1; animation-timing-function: ease; }
  88%, 100% { opacity: 0; scale: 1; }
}
@keyframes td-dim { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }
@keyframes td-eq {
  0%, 100% { transform: none; }
  14% { transform: scaleY(0.45); }
  32% { transform: scaleY(1); }
  52% { transform: scaleY(0.5); }
  72% { transform: scaleY(0.95); }
  88% { transform: scaleY(0.6); }
}
@keyframes td-node { 0%, 100% { opacity: 0; } 45% { opacity: 1; } }
@keyframes td-land {
  0%, 55% { background: #E4E6FF; border-color: #B9BBF6; }
}
@keyframes td-ready {
  0%, 100% { box-shadow: inset 0 0 0 1px #D6DAFB; }
  40% { box-shadow: inset 0 0 0 1.4px #A7A9F0, 0 0 0 4px rgba(91, 92, 230, 0.16); }
}
@keyframes td-type {
  0% { max-width: 0; opacity: 1; border-right-color: #5B5CE6; }
  62%, 78% { max-width: 100%; opacity: 1; border-right-color: #5B5CE6; }
  79%, 100% { max-width: 0; opacity: 0; border-right-color: transparent; }
}
@keyframes td-ph { 0%, 78% { opacity: 0; } 79%, 100% { opacity: 1; } }
@keyframes td-send { 0% { background: #C4C8D6; } 8%, 78% { background: #5B5CE6; } 79%, 100% { background: #C4C8D6; } }

/* --- 1024 previews (~481 px body): the checklist and the suggestions give way ----- */

@container (max-width: 500px) {
  .mkt-widget-body.mkt-td { padding: 12px 13px; gap: 8px; }
  .mkt-td-title { font-size: 12.5px; }
  .mkt-td-hash { font-size: 11px; }
  .mkt-td-pill { font-size: 9.2px; padding: 2px 7px; }
  .mkt-td-fact { padding: 7px 8px; }
  .mkt-td-fact dt { font-size: 8.2px; letter-spacing: 0.03em; }
  .mkt-td-fact dd { min-height: 30px; }
  .mkt-td-fact-strong { font-size: 10.5px; }
  .mkt-td-status { font-size: 8.8px; gap: 5px; }
  .mkt-td-box { width: 12px; height: 12px; border-radius: 3.5px; }
  .mkt-td-ring { width: 30px; height: 30px; }
  .mkt-td-ring .mkt-td-num { font-size: 10.5px; }
  .mkt-td-delta { font-size: 9.2px; padding: 2px 5px; }
  .mkt-td-split { gap: 8px; }
  /* The bullets give way, not the prose: with the comment field and the completion button gone the
     pane keeps all three things that explain the task — what to do, the effect it buys and the
     pages it touches — plus the checklist the status control ticks off. BIA's suggestions go too;
     the exchange that changes the task does not need them to read. */
  .mkt-td-sec.is-affects,
  .mkt-td-chips { display: none; }
  .mkt-td-p { font-size: 10.5px; -webkit-line-clamp: 3; }
  .mkt-td-list li { font-size: 10.5px; }
  /* Too narrow for the label and both paths on one line: the kicker takes a row of its own, the
     shape every other section in the pane already has. */
  .mkt-td-sec.is-pages .mkt-td-kicker { flex: 1 0 100%; }
  .mkt-td-sec.is-pages { gap: 4px; }
  .mkt-td-pane .mkt-path-chip { font-size: 9.2px; padding: 1.5px 6px; }
  .mkt-td-refine { font-size: 10.2px; padding: 6px 8px; }
  .mkt-td-chat { padding: 8px; gap: 7px; }
  .mkt-td-chat-h { font-size: 10.5px; }
  .mkt-td-msg { font-size: 10.2px; padding: 6px 8px; }
  /* Shorter column, fewer messages — and with BIA's suggestions hidden only three gaps to spend the
     spare height on, so each one is a little wider than the desktop tier's. */
  .mkt-td-thread { --td-gap-b: 21px; }
  .mkt-td-think { font-size: 9.6px; }
  .mkt-td-note { font-size: 9px; }
  .mkt-td-ph, .mkt-td-typed { font-size: 10px; }
}

/* --- phone previews (~328 px body): the task, its facts and the exchange that changes it */

@container (max-width: 430px) {
  .mkt-widget-body.mkt-td { padding: 9px 10px; gap: 6px; }
  .mkt-td-pane { gap: 6px; }
  .mkt-td-title { font-size: 11.5px; line-height: 1.3; }
  .mkt-td-hash { font-size: 10px; margin-right: 4px; }
  .mkt-td-pills { gap: 4px; }
  .mkt-td-pill { font-size: 8.4px; padding: 1.5px 6px; }
  .mkt-td-facts { border-radius: 10px; }
  .mkt-td-fact { padding: 5px; gap: 2px; }
  .mkt-td-fact dt { font-size: 7.4px; letter-spacing: 0; }
  .mkt-td-fact dd { min-height: 22px; gap: 4px; overflow: hidden; }
  .mkt-td-fact-strong { font-size: 9.5px; }
  .mkt-td-status { font-size: 8px; gap: 4px; }
  .mkt-td-box { width: 10px; height: 10px; border-radius: 3px; }
  .mkt-td-ring { width: 26px; height: 26px; }
  .mkt-td-ring circle { stroke-width: 5; }
  .mkt-td-ring .mkt-td-num { font-size: 9.5px; }
  .mkt-td-delta { font-size: 8.4px; padding: 1.5px 5px; gap: 2px; }
  .mkt-td-delta svg { width: 6.5px; height: 6.5px; }
  .mkt-td-delta .is-full { display: none; }
  .mkt-td-delta .is-short { display: inline; }
  .mkt-td-split { grid-template-columns: 1fr 0.95fr; gap: 7px; }
  .mkt-td-kicker { font-size: 8px; gap: 4px; }
  .mkt-td-upd { font-size: 7.2px; padding: 1.5px 4px; }
  .mkt-td-p { font-size: 9.8px; line-height: 1.45; margin-top: 2px; -webkit-line-clamp: 3; }
  .mkt-td-refine { font-size: 9.4px; line-height: 1.4; padding: 5px 6px; border-radius: 8px; }
  .mkt-td-refine-label { font-size: 7.2px; margin-bottom: 2px; }
  /* The phone thread keeps the exchange that changes the task — the question and BIA's answer.
     The payoff is on the left, where the refined description and its "Zaktualizowano" badge sit.
     Both of the pane's text blocks switch to their phone-length half here so each one finishes its
     sentence inside three lines: at this width the full wording needs a fourth line the column
     cannot give, and a clamped line plus the air under it reads worse than a shorter sentence. */
  .mkt-td-ctx,
  .mkt-td-sec.is-why,
  .mkt-td-sec.is-check,
  .mkt-td-slot.is-note,
  .mkt-td-thread > .mkt-td-msg { display: none; }
  .mkt-td-msg .is-full,
  .mkt-td-p .is-full,
  .mkt-td-refine-text .is-full { display: none; }
  .mkt-td-msg .is-short,
  .mkt-td-p .is-short,
  .mkt-td-refine-text .is-short { display: inline; }
  .mkt-td-pane .mkt-path-chip { font-size: 8.4px; padding: 1px 5px; border-radius: 5px; }
  .mkt-td-chat { padding: 7px; gap: 6px; border-radius: 10px; }
  .mkt-td-chat-h { font-size: 9.6px; gap: 5px; }
  .mkt-td-msg { font-size: 9.4px; padding: 5px 7px; border-radius: 9px; }
  .mkt-td-thread { --td-gap-b: 8px; }
  .mkt-td-think { font-size: 8.8px; }
  .mkt-td-note { font-size: 8.4px; padding: 4px 6px; }
  .mkt-td-note svg { width: 9px; height: 9px; }
  .mkt-td-compose { gap: 5px; padding-top: 6px; }
  .mkt-td-field { height: 14px; }
  .mkt-td-ph, .mkt-td-typed { font-size: 9.2px; line-height: 14px; }
  .mkt-td-send { width: 20px; height: 20px; border-radius: 6px; }
  .mkt-td-send svg { width: 9px; height: 9px; }
}

/* --- the wide preview (one-column step layout, ~662 px body) --------------------- */

@container (min-width: 620px) {
  .mkt-widget-body.mkt-td { padding: 16px 20px; gap: 11px; }
  .mkt-td-pane { gap: 7px; }
  .mkt-td-title { font-size: 17px; }
  .mkt-td-hash { font-size: 14px; }
  .mkt-td-pill { font-size: 11.5px; padding: 3.5px 10px; }
  .mkt-td-fact { padding: 12px 14px; }
  .mkt-td-fact dt { font-size: 10px; }
  .mkt-td-fact dd { min-height: 40px; }
  .mkt-td-fact-strong { font-size: 13.5px; }
  .mkt-td-status { font-size: 11px; gap: 8px; }
  .mkt-td-box { width: 17px; height: 17px; border-radius: 5px; }
  .mkt-td-ring { width: 40px; height: 40px; }
  .mkt-td-ring .mkt-td-num { font-size: 14px; }
  .mkt-td-delta { font-size: 11.5px; padding: 3px 8px; }
  .mkt-td-delta svg { width: 9px; height: 9px; }
  .mkt-td-split { grid-template-columns: 1fr 0.82fr; gap: 14px; }
  .mkt-td-kicker { font-size: 10.5px; }
  .mkt-td-upd { font-size: 9.5px; }
  .mkt-td-p { font-size: 13.5px; }
  .mkt-td-refine { font-size: 13px; padding: 8px 11px; border-radius: 11px; }
  .mkt-td-refine-label { font-size: 9.5px; }
  .mkt-td-list li { font-size: 13px; padding-left: 15px; }
  .mkt-td-list li::before { top: 7px; }
  .mkt-td-sec.is-pages { gap: 8px; }
  .mkt-td-pane .mkt-path-chip { font-size: 12px; padding: 3px 9px; }
  .mkt-td-check li { font-size: 13px; gap: 8px; }
  .mkt-td-check li i { width: 13px; height: 13px; margin-top: 3px; border-radius: 4px; }
  .mkt-td-chat { padding: 14px; }
  .mkt-td-chat-h { font-size: 13.5px; }
  .mkt-td-ctx-chip { font-size: 10px; padding: 3px 9px; }
  .mkt-td-msg { font-size: 13px; padding: 9px 11px; border-radius: 13px; }
  .mkt-td-chips { gap: 6px; }
  .mkt-td-thread { --td-gap-b: 21px; }
  .mkt-td-chips-label { font-size: 9.5px; }
  .mkt-td-chip { font-size: 11.5px; padding: 4px 11px; }
  .mkt-td-think { font-size: 12.5px; }
  .mkt-td-note { font-size: 11.5px; padding: 6px 9px; }
  .mkt-td-note svg { width: 12px; height: 12px; }
  .mkt-td-field { height: 19px; }
  .mkt-td-ph, .mkt-td-typed { font-size: 13px; line-height: 19px; }
  .mkt-td-send { width: 28px; height: 28px; }
  .mkt-td-send svg { width: 13px; height: 13px; }
}

/* === task-list ============================================================= */
/*
 * Product page preview — /produkt-claude-preview, step 3 "Działaj" → "Rekomendowane taski".
 * Widget: resources/themes/anchor/partials/marketing/widgets/v2/task-list.blade.php
 *
 * Every rule below describes the SETTLED list: task 1 closed and already added to the Task Manager
 * (green tick), task 2 open on its detail, the rest of the report continuing under a soft fade. The
 * sequence that leads there is a set of keyframes whose early frames hold the earlier states. They
 * only apply once the widget is armed by its inline script, stay paused until marketing.js marks
 * the widget live, and restart by themselves each time the accordion shows the widget again
 * (display: none cancels animations).
 *
 * The frame is two blocks, header and list — no filter bar, no "sorted by" label, no
 * "pokazano x z y" line and no pager. What is left is what a viewer reads in three seconds: how big
 * the report is, which task is first, and that the list keeps going. The rows are wider apart and
 * deeper than they were, so fewer of them fill the frame and each one has room to be read.
 *
 * Timeline (s): 0–0.6 ghost rows hold the list's shape while the count ticks to 21 ·
 * 0.38–1.28 the real rows fill in · 1.05–1.50 the fade arrives ·
 * 1.25–1.70 task 1 expands · 2.10 it is added to the board (+ → ✓) · 2.50–2.90 it closes ·
 * 2.80–3.25 task 2 expands and stays open.
 */

@property --rt-n { syntax: '<integer>'; inherits: false; initial-value: 0; }

/* A size container, not an inline-size one: the vertical rhythm below is written in cqh so it
   follows the height of the frame the widget was dropped into. `.mkt-widget-body` takes its
   height from the fixed stage above it (`flex: 1` on a definite-height flex column), so nothing
   here depends on its own content and the containment is safe. */
.mkt-rt { gap: 12px; container-type: size; }

.mkt-rt-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.mkt-rt-num { --rt-n: var(--to); counter-reset: rt var(--rt-n); }
.mkt-rt-num::after { content: counter(rt); }

/* --- header ------------------------------------------------------------------ */

.mkt-rt-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; }
.mkt-rt-head-copy { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.mkt-rt-head-copy strong { font-size: 14px; font-weight: 600; letter-spacing: -0.015em; color: #14162B; }
.mkt-rt-head-copy > span { font-size: 11.5px; line-height: 1.35; color: #656B7E; text-wrap: pretty; }
.mkt-rt-count {
  display: flex;
  align-items: baseline;
  gap: 5px;
  flex: 0 0 auto;
  max-width: 40%;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.25;
  color: #656B7E;
  text-align: right;
}
.mkt-rt-count b { font-size: 15px; font-weight: 600; letter-spacing: 0; color: #14162B; }

/* --- the list ------------------------------------------------------------------
 * The frame shows the first rows; the rest keeps going under the fade. Rows are pushed
 * down as one of them expands, which is exactly the "there is more below" cue. */

.mkt-rt-scroll {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;

  /* The vertical rhythm. It is stated once, here, in cqh — the height of the frame — because the
     one thing this preview has to get right is that the list runs OUT of the frame: a list that
     stops inside it says the opposite of what it is about. Fixed paddings tuned on the tall
     1/1 product preview left the 16/10 home stage ending on a whole card, and a word of copy
     changing the open task's height was enough to move the cut anywhere. Tied to the frame,
     each frame keeps roughly the same number of rows and the same place in a card to be cut,
     from the 279px phone preview to the 623px one at 768. The horizontal padding is not in
     here: that still follows the width, in the blocks further down. */
  --rt-pad-x: 12px;
  --rt-gap: clamp(6px, 1.4cqh, 9px);
  --rt-pad-y: clamp(8px, 2.5cqh, 16px);
  --rt-meta-y: clamp(4px, 1.2cqh, 8px);
  --rt-det-t: clamp(8px, 2.2cqh, 14px);
  --rt-det-b: clamp(9px, 2.4cqh, 15px);
  /* Deep enough to swallow a whole card, not just the gap between two. Where the frame's edge
     falls in the row cycle is arithmetic no rhythm can steer: it is the frame height minus the
     open task, modulo one row plus one gap, and the eight real frames (two pages x five
     viewports) land all over that cycle — one of them squarely in a gap, so the report looked
     like it held four tasks. The cue that it goes on therefore cannot depend on catching a card
     at half height. At this depth the last card above the edge has always lost its pills and its
     summary to the fade, whether the edge cuts it or lands just past its bottom. */
  --rt-fade: clamp(34px, 13cqh, 74px);
}
.mkt-rt-list { display: flex; flex-direction: column; gap: var(--rt-gap); }
.mkt-rt-fade {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--rt-fade);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.86) 58%, #FFFFFF 100%);
  pointer-events: none;
}

/* Ghost rows. The frame is never blank: until the real rows arrive these hold the list's shape, so
   the held first frame reads as a screen about to fill rather than as a widget that failed to load.
   They exist only while the sequence is armed, and crossfade into the rows at ~0.3–0.6 s. */
.mkt-rt-skeleton {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: var(--rt-gap);
  pointer-events: none;
}
.mkt-rt-skeleton i {
  position: relative;
  display: block;
  flex: 0 0 auto;
  height: 66px;
  border: 1px solid #F1F2F6;
  border-left: 3px solid #E9EBF2;
  border-radius: 10px;
  background: #FFFFFF;
}
.mkt-rt-skeleton i::before,
.mkt-rt-skeleton i::after { content: ""; position: absolute; left: 10px; }
.mkt-rt-skeleton i::before { top: 16px; height: 8px; width: var(--w, 54%); border-radius: 4px; background: #EEEFF5; }
.mkt-rt-skeleton i::after { top: 38px; height: 13px; width: var(--w2, 46%); border-radius: 999px; background: #F5F6FA; }
.mkt-rt-skeleton i:nth-child(2) { --w: 68%; --w2: 40%; }
.mkt-rt-skeleton i:nth-child(3) { --w: 47%; --w2: 52%; }
.mkt-rt-skeleton i:nth-child(4) { --w: 61%; --w2: 38%; }
.mkt-rt-skeleton i:nth-child(5) { --w: 43%; --w2: 50%; }
.mkt-rt-skeleton i:nth-child(6) { --w: 57%; --w2: 44%; }
.mkt-rt-skeleton i:nth-child(7) { --w: 50%; --w2: 48%; }
.mkt-rt-skeleton i:nth-child(8) { --w: 64%; --w2: 41%; }
/* No scripts → never armed → the settled list is the only thing on screen. */
.mkt-rt:not(.is-armed) .mkt-rt-skeleton { display: none; }

.mkt-rt-row {
  border: 1px solid #EDEEF2;
  border-left-width: 3px;
  border-radius: 10px;
  background: #FFFFFF;
}
.mkt-rt-row.is-high { border-left-color: #EF4444; }
.mkt-rt-row.is-med { border-left-color: #F59E0B; }
.mkt-rt-row.is-low { border-left-color: #C4C8D6; }
.mkt-rt-row.is-open {
  background: #F8F8FE;
  border-top-color: #D6DAFB;
  border-right-color: #D6DAFB;
  border-bottom-color: #D6DAFB;
}

.mkt-rt-row-head { display: flex; align-items: center; gap: 8px; padding: var(--rt-pad-y) var(--rt-pad-x) 0; }
.mkt-rt-rank {
  width: 17px;
  height: 17px;
  border-radius: 999px;
  background: #F4F5F8;
  color: #656B7E;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
}
.mkt-rt-row.is-open .mkt-rt-rank { background: #E6E7FB; color: #4B4CD8; }
.mkt-rt-title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.3;
  color: #14162B;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mkt-rt-more {
  flex: 0 0 auto;
  font-size: 10px;
  font-weight: 600;
  color: #FFFFFF;
  background: #5B5CE6;
  border-radius: 7px;
  padding: 4px 9px;
  white-space: nowrap;
}
.mkt-rt-row.is-open .mkt-rt-more { color: #4B4CD8; background: #FFFFFF; box-shadow: inset 0 0 0 1px #D6DAFB; }
/* The phone frame shortens the label rather than the task title next to it. */
.mkt-rt-more-short { display: none; }

.mkt-rt-add {
  position: relative;
  width: 20px;
  height: 20px;
  border-radius: 6px;
  flex: 0 0 auto;
  background: #FFFFFF;
  box-shadow: inset 0 0 0 1px #D6DAFB;
}
.mkt-rt-add i { position: absolute; inset: 0; }
.mkt-rt-add .is-plus::before,
.mkt-rt-add .is-plus::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 8px;
  height: 1.6px;
  border-radius: 1px;
  background: #4B4CD8;
  translate: -50% -50%;
}
.mkt-rt-add .is-plus::after { rotate: 90deg; }
.mkt-rt-add .is-check { opacity: 0; }
.mkt-rt-add .is-check::after {
  content: "";
  position: absolute;
  left: 7px;
  top: 4.4px;
  width: 4px;
  height: 7.4px;
  border: solid #FFFFFF;
  border-width: 0 1.7px 1.7px 0;
  transform: rotate(45deg);
}
.mkt-rt-row.is-added .mkt-rt-add { background: #22C55E; box-shadow: none; }
.mkt-rt-row.is-added .mkt-rt-add .is-plus { opacity: 0; }
.mkt-rt-row.is-added .mkt-rt-add .is-check { opacity: 1; }

.mkt-rt-meta { display: flex; align-items: center; gap: 7px; padding: var(--rt-meta-y) var(--rt-pad-x) var(--rt-pad-y); min-width: 0; }
.mkt-rt-tag {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8.5px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 1px solid;
  border-radius: 999px;
  padding: 2px 7px;
  white-space: nowrap;
  flex: 0 0 auto;
}
.mkt-rt-tag.is-i-high { color: #B91C1C; background: #FEF3F3; border-color: #F6BDBD; }
.mkt-rt-tag.is-i-med { color: #B45309; background: #FFFAEC; border-color: #F5D28A; }
.mkt-rt-tag.is-i-low { color: #656B7E; background: #F6F7FA; border-color: #E3E5EC; }
.mkt-rt-tag.is-diff {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: #5F6478;
  background: #FFFFFF;
  border-color: #E3E5EC;
}
/* Dot tones follow the app's difficultyTone(): easy → success, medium → default (grey),
   hard → warning (amber). Amber for "Trudne" also keeps a second red off rows already
   carrying a red "Krytyczny" pill. */
.mkt-rt-tag.is-diff::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: #22C55E; }
.mkt-rt-tag.is-d-med::before { background: #C4C8D6; }
.mkt-rt-tag.is-d-hard::before { background: #F59E0B; }
.mkt-rt-desc {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 11px;
  line-height: 1.3;
  color: #7C8196;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* --- the expanded task ---------------------------------------------------------
 * 0fr → 1fr on a grid row is the accordion: the detail keeps its natural height and the
 * rows underneath move with it. */

.mkt-rt-detail { display: grid; grid-template-rows: 0fr; }
.mkt-rt-row.is-open .mkt-rt-detail { grid-template-rows: 1fr; }
.mkt-rt-detail-in { overflow: hidden; min-height: 0; }
.mkt-rt-detail-pad {
  margin: 0 var(--rt-pad-x);
  padding: var(--rt-det-t) 0 var(--rt-det-b);
  border-top: 1px solid #E7E8F5;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
.mkt-rt-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.mkt-rt .mkt-widget-kicker { font-size: 9px; letter-spacing: 0.1em; }
/* marketing.css ends its mobile block with `p { font-size: 14px !important }`, which at the
   phone viewport makes this sentence larger than the widget's own title. Only !important beats
   !important, and the class wins the tie on specificity. */
.mkt-rt-p {
  margin: 4px 0 0;
  font-size: 11px !important;
  line-height: 1.45;
  color: #4C5163;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* The phone frame has room for one line of this, so it swaps in a shorter wording of the same
   instruction and ends on a full clause instead of mid-sentence. */
.mkt-rt-p.is-short { display: none; }
.mkt-rt-affected { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.mkt-rt-affected .mkt-path-chip { font-size: 10px; padding: 2px 7px; }

/* The widest frames (the 768 viewport preview and the home stage from 1000px up) carry the wider
 * card padding. The air inside the card comes from the rhythm above, which these frames are tall
 * enough to take at its top end. */
@container (min-width: 600px) {
  .mkt-rt-scroll { --rt-pad-x: 14px; }
  .mkt-rt-skeleton i { height: 78px; }
}

/* --- narrow previews ------------------------------------------------------------
 * The phone frame keeps the same list; only the parts that would collide are dropped. */

/* 456px is the narrowest desktop frame (the 1024 viewport). Drop the subtitle and tighten the
   spacing there, so the next card still starts under the fade: a list that ends inside the frame
   would say the opposite of what this preview is about. */
@container (max-width: 470px) {
  .mkt-rt { gap: 10px; }
  /* Below this width a task title no longer holds one line — and the title is what the row is
     for. It wraps to two lines, and the inline summary it was competing with steps aside. */
  .mkt-rt-head-copy > span,
  .mkt-rt-desc { display: none; }
  .mkt-rt-title {
    white-space: normal;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    /* Hold two lines even when a title only needs one. Polish titles wrap here and English ones
       often do not, so without this the rows come out at two different heights per language and
       the frame's bottom edge lands somewhere else in each — sometimes in the gap between cards,
       which makes the list look like it ends. Two lines everywhere keeps the rhythm even and the
       cut card in the same place in both. (min-height in `lh` is ignored by browsers that do not
       know the unit; they get today's uneven rows, not a broken frame.) */
    min-height: 2lh;
  }
  /* Two-line titles cost height; the narrower card padding gives some of it back. */
  .mkt-rt-scroll { --rt-pad-x: 11px; }
  .mkt-rt-cols { gap: 9px; }
}

/* The 467px desktop frame (the 1024 viewport) is the tightest of the wide frames: two-line titles
   cost it the most, so its title lines sit a little closer together. */
@container (min-width: 381px) and (max-width: 470px) {
  .mkt-rt-title { line-height: 1.25; }
}

/* Phone frame (~348px): the open task keeps two lines of "Co zrobić" only. Otherwise the frame ends
   inside the open task's own copy, which reads as clipped content instead of "the list goes on".
   The count stays: with the footer gone it is the only place the report's size is stated, and the
   subtitle it used to compete with for the header line is already hidden above. */
@container (max-width: 380px) {
  .mkt-rt-cols > div + div,
  .mkt-rt-affected,
  .mkt-rt-p:not(.is-short) { display: none; }
  .mkt-rt-p.is-short { display: -webkit-box; -webkit-line-clamp: 2; }
  .mkt-rt-head-copy strong { font-size: 13px; }
  /* Two-line titles cost height, so everything else in the row gives a little back: the open
     task and a readable slice of the next card both have to fit a 348px frame. */
  .mkt-rt-scroll { --rt-pad-x: 10px; }
  .mkt-rt-title { font-size: 11px; line-height: 1.2; }
  .mkt-rt-row-head { gap: 7px; }
  .mkt-rt-meta { gap: 5px; }
  .mkt-rt-tag { padding: 1.5px 6px; }
  .mkt-rt-more { font-size: 9px; padding: 3.5px 7px; }
  .mkt-rt-more-long { display: none; }
  .mkt-rt-more-short { display: inline; }
  .mkt-rt-rank { width: 16px; height: 16px; }
  .mkt-rt-cols { grid-template-columns: 1fr; }
  .mkt-rt-p { font-size: 10.5px !important; line-height: 1.4; }
  .mkt-rt-count { font-size: 9px; letter-spacing: 0.04em; max-width: 48%; }
  .mkt-rt-count b { font-size: 13px; }
  .mkt-rt-skeleton i { height: 62px; }
}

/* Sub-phone frames only. The 390px viewport leaves this container exactly 320px, so a
   `max-width: 320px` query fired on the phone itself and dropped the rank badges the Task
   Manager and the task detail keep as #1 / #4 at that same width. Below ~370px of viewport
   the row runs out of width for the badge and the two-line title together, and the badge is
   the part the frame can lose. */
@container (max-width: 300px) {
  .mkt-rt-rank { display: none; }
}

/* --- sequence --------------------------------------------------------------------
 * Delays are literal seconds so the whole story stays readable in one place. */

/* The header is never animated away: the paused first frame has to read as the screen itself,
   waiting for its rows — not as an empty white box. */
.mkt-rt.is-armed .mkt-rt-skeleton { animation: rt-skel-out 0.3s ease 0.3s both; }
.mkt-rt.is-armed .mkt-rt-num { animation: rt-count 0.9s cubic-bezier(0.22, 1, 0.36, 1) 0.12s both; }
.mkt-rt.is-armed .mkt-rt-row { animation: rt-rise 0.42s cubic-bezier(0.22, 1, 0.36, 1) calc(0.38s + var(--i) * 0.075s) both; }
.mkt-rt.is-armed .mkt-rt-fade { animation: rt-fade 0.45s ease 1.05s both; }

/* Task 1: opens, is added to the board, then closes again — its base state is "closed, added".
   The detail copy arrives with the box, never after it, so the box is never open and empty. */
.mkt-rt.is-armed .mkt-rt-row.is-first .mkt-rt-detail { animation: rt-sweep 1.65s linear 1.25s both; }
.mkt-rt.is-armed .mkt-rt-row.is-first { animation: rt-rise 0.42s cubic-bezier(0.22, 1, 0.36, 1) 0.38s both, rt-sweep-skin 1.65s linear 1.25s both; }
.mkt-rt.is-armed .mkt-rt-row.is-first .mkt-rt-rank { animation: rt-sweep-rank 1.65s linear 1.25s both; }
.mkt-rt.is-armed .mkt-rt-row.is-first .mkt-rt-more { animation: rt-sweep-btn 1.65s linear 1.25s both; }
.mkt-rt.is-armed .mkt-rt-row.is-first .mkt-rt-detail-pad > * { animation: rt-rise 0.34s cubic-bezier(0.22, 1, 0.36, 1) calc(1.26s + var(--j, 0) * 0.09s) both; }
.mkt-rt.is-armed .mkt-rt-row.is-first .mkt-rt-cols { --j: 0; }
.mkt-rt.is-armed .mkt-rt-row.is-first .mkt-rt-affected { --j: 1; }
.mkt-rt.is-armed .mkt-rt-row.is-first .mkt-rt-add { animation: rt-add 2.35s linear both; }
.mkt-rt.is-armed .mkt-rt-row.is-first .mkt-rt-add .is-plus { animation: rt-plus-out 2.25s linear both; }
.mkt-rt.is-armed .mkt-rt-row.is-first .mkt-rt-add .is-check { animation: rt-check 0.42s cubic-bezier(0.2, 0.9, 0.3, 1.4) 2.1s both; }

/* Task 2: stays closed until task 1 is done with, then opens and stays open. */
.mkt-rt.is-armed .mkt-rt-row.is-open .mkt-rt-detail { animation: rt-open 0.45s cubic-bezier(0.22, 1, 0.36, 1) 2.8s both; }
.mkt-rt.is-armed .mkt-rt-row.is-open { animation: rt-rise 0.42s cubic-bezier(0.22, 1, 0.36, 1) 0.455s both, rt-skin 0.45s ease 2.8s both; }
.mkt-rt.is-armed .mkt-rt-row.is-open .mkt-rt-rank { animation: rt-rank 0.45s ease 2.8s both; }
.mkt-rt.is-armed .mkt-rt-row.is-open .mkt-rt-more { animation: rt-btn 0.4s ease 2.85s both; }
.mkt-rt.is-armed .mkt-rt-row.is-open .mkt-rt-detail-pad > * { animation: rt-rise 0.34s cubic-bezier(0.22, 1, 0.36, 1) calc(2.82s + var(--j, 0) * 0.09s) both; }
.mkt-rt.is-armed .mkt-rt-row.is-open .mkt-rt-cols { --j: 0; }
.mkt-rt.is-armed .mkt-rt-row.is-open .mkt-rt-affected { --j: 1; }

/* Waiting for marketing.js to mark the widget live: hold the first frame. */
.mkt-widget:not(.is-live) .mkt-rt.is-armed *,
.mkt-widget:not(.is-live) .mkt-rt.is-armed *::before,
.mkt-widget:not(.is-live) .mkt-rt.is-armed *::after { animation-play-state: paused !important; }

@media (prefers-reduced-motion: reduce) {
  .mkt-rt *,
  .mkt-rt *::before,
  .mkt-rt *::after { animation: none !important; }
  /* Without its fade-out the skeleton would sit on top of the settled list. */
  .mkt-rt-skeleton { display: none !important; }
}

@keyframes rt-skel-out { to { opacity: 0; } }
@keyframes rt-rise { from { opacity: 0; translate: 0 7px; } }
@keyframes rt-fade { from { opacity: 0; } }
@keyframes rt-count { from { --rt-n: 0; } }

@keyframes rt-open { from { grid-template-rows: 0fr; } }
@keyframes rt-skin { from { background: #FFFFFF; border-top-color: #EDEEF2; border-right-color: #EDEEF2; border-bottom-color: #EDEEF2; } }
@keyframes rt-rank { from { background: #F4F5F8; color: #656B7E; } }
@keyframes rt-btn { from { color: #FFFFFF; background: #5B5CE6; box-shadow: inset 0 0 0 1px #5B5CE6; } }

@keyframes rt-sweep {
  0% { grid-template-rows: 0fr; animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
  27% { grid-template-rows: 1fr; }
  76% { grid-template-rows: 1fr; animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }
  100% { grid-template-rows: 0fr; }
}
@keyframes rt-sweep-skin {
  0%, 100% { background: #FFFFFF; border-top-color: #EDEEF2; border-right-color: #EDEEF2; border-bottom-color: #EDEEF2; }
  18%, 85% { background: #F8F8FE; border-top-color: #D6DAFB; border-right-color: #D6DAFB; border-bottom-color: #D6DAFB; }
}
@keyframes rt-sweep-rank {
  0%, 100% { background: #F4F5F8; color: #656B7E; }
  18%, 85% { background: #E6E7FB; color: #4B4CD8; }
}
@keyframes rt-sweep-btn {
  0%, 100% { color: #FFFFFF; background: #5B5CE6; box-shadow: inset 0 0 0 1px #5B5CE6; }
  18%, 85% { color: #4B4CD8; background: #FFFFFF; box-shadow: inset 0 0 0 1px #D6DAFB; }
}
@keyframes rt-add { 0%, 90% { background: #FFFFFF; box-shadow: inset 0 0 0 1px #D6DAFB; } }
@keyframes rt-plus-out { 0%, 93% { opacity: 1; scale: 1; } 100% { opacity: 0; scale: 0.6; } }
@keyframes rt-check { from { opacity: 0; scale: 0.4; } }

/* === visibility-score ====================================================== */
/*
 * Product page preview — Monitoring → "Widoczność" (widgets/v2/visibility-score.blade.php).
 *
 * Every rule below describes the FINISHED view: the average visibility score, the KPI summary and
 * the mentions chart for the eight days with its key. The run that leads there is a set of
 * keyframes whose early frames hold the earlier states. They apply only once the widget is armed by
 * its inline script, sit paused until marketing.js marks the widget live, and restart by themselves
 * each time the accordion shows the widget again (display: none cancels CSS animations).
 *
 * The head holds one slot twice over: the status line narrates in it and then hands it to the score
 * the run was after. Nothing about the run survives into the settled frame — no spinner, and no
 * "monitoring updated" tick, which is not in the markup at all — and the slot is never empty,
 * because the result takes it over.
 *
 * The narration is ONE line, and there is no "done" beat at all: the score is the result, so a
 * "monitoring updated" alert would only delay it. That also keeps the whole sequence inside the
 * three seconds the home page's SYSTEM tabs give a panel (marketing.js rotates them on a 3000 ms
 * interval), so the tab named "Widoczność" always rests on the visibility score, not mid-run.
 *
 * Three bands, not four: the head, the app's summary tiles and the mentions chart with its key.
 * The share donut's per-model list is gone — the app drops that donut at preview width (the
 * `compact` branch of brand-mentions-chart-card.tsx) and the chart's own legend names the models.
 *
 * Timeline (s): 0–0.40 the view is set up (filters, axis, the key, tiles with their loaders) ·
 *               0.10–1.05 the mentions chart fills day by day while the mentions total counts ·
 *               0.80–1.30 the share and the position settle ·
 *               0–1.40 the status line runs under all of it and then clears the slot ·
 *               1.40–2.10 the score sweeps into the place it has left, closing on 72 ·
 *               2.00 the halo, 2.05 the ↑ 4 — settled by 2.35 s.
 *
 * .mkt-vs is the widget body and only the container the queries measure — a container query never
 * matches the container element itself, so the layout lives on .mkt-vs-stage inside it.
 * Class prefix mkt-vs-. The only shared classes reused are .mkt-delta (change indicator) and
 * .mkt-llm (model logo); both are only ever overridden under .mkt-vs.
 */

@property --vs-n { syntax: '<integer>'; inherits: false; initial-value: 0; }
@property --vs-d { syntax: '<integer>'; inherits: false; initial-value: 0; }

.mkt-vs {
  container-type: inline-size;
  --sep: ',';
  --ink: #14162B;
  --muted: #656B7E;
  --line: #EDEEF2;
  --ok: #22C55E;
}
.mkt-vs.is-dot { --sep: '.'; }

.mkt-vs-stage {
  display: flex;
  flex-direction: column;
  gap: 13px;
  flex: 1 1 auto;
  min-height: 0;
}

.mkt-vs-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Counters: the integer sits in --to (and the tenth in --tod); the real number is in the
   visually hidden span, so a blocked stylesheet still reads correctly. */
.mkt-vs-num { --vs-n: var(--to); --vs-d: var(--tod, 0); counter-reset: vsn var(--vs-n) vsd var(--vs-d); }
.mkt-vs-num::after { content: counter(vsn); }
.mkt-vs-num.is-dec::after { content: counter(vsn) var(--sep) counter(vsd); }

.mkt-vs-cap {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mkt-vs-cap.is-long { display: none; }

/* --- the head: the score, and the run that hands the slot over to it ------------ */

.mkt-vs-top { display: flex; align-items: center; gap: 12px; flex: 0 0 auto; }
/* One grid cell holding both states, so the handover cross-fades instead of reflowing. */
.mkt-vs-lead { display: grid; flex: 1 1 auto; min-width: 0; }
.mkt-vs-lead > * { grid-area: 1 / 1; align-self: center; min-width: 0; }

.mkt-vs-score { display: flex; align-items: center; gap: 10px; }
.mkt-vs-ring { position: relative; width: 44px; height: 44px; flex: 0 0 auto; }
.mkt-vs-ring svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
.mkt-vs-ring circle { fill: none; stroke-width: 4; }
.mkt-vs-ring .is-track { stroke: #EDEEF2; }
/* The arc is a dash of --score units on a 100-unit path (not a full dash pushed by an offset):
   at zero length the round cap then paints nothing, instead of a stray dot at 12 o'clock. */
.mkt-vs-ring .is-value { stroke: var(--ok); stroke-linecap: round; stroke-dasharray: var(--score) 100; stroke-dashoffset: 0; }
.mkt-vs-halo {
  position: absolute;
  inset: -3px;
  border-radius: 50%;
  box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.55);
  opacity: 0;
  pointer-events: none;
}
.mkt-vs-ring .mkt-vs-num {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15.5px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.mkt-vs-score-copy { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.mkt-vs .mkt-vs-score-copy .mkt-delta { font-size: 11px; }

/* The narration. Its base state is "already over": no script, or reduced motion, and the head
   simply reads the score. */
.mkt-vs-run { display: flex; align-items: center; gap: 8px; opacity: 0; pointer-events: none; }
.mkt-vs-spinner {
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  border-radius: 50%;
  border: 2px solid #E3E4FA;
  border-top-color: #5B5CE6;
}
.mkt-vs-steps { display: flex; flex: 1 1 auto; min-width: 0; font-size: 12.5px; font-weight: 500; color: #262A47; }
/* No opacity of their own: .mkt-vs-run above is what holds the whole row out of the settled
   frame, and a child parked at 0 would make an animation filled `both` resolve its own
   to-state to 0 — i.e. fade in to nothing. */
.mkt-vs-steps > span {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mkt-vs-filters { display: flex; gap: 6px; flex: 0 0 auto; }
.mkt-vs-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border: 1px solid #E3E5EC;
  border-radius: 999px;
  background: #FFFFFF;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px;
  color: #4C5163;
  white-space: nowrap;
}
.mkt-vs-chip::after {
  content: "";
  width: 4px;
  height: 4px;
  border: solid #9AA0B4;
  border-width: 0 1.2px 1.2px 0;
  transform: translateY(-1px) rotate(45deg);
}

/* --- KPI summary ---------------------------------------------------------------
 * The three tiles of the app's visibility summary: share of voice, brand mentions and the
 * average position. A label and a number, nothing else — the same box and the same reading as
 * the tiles in the sibling query-list preview (.mkt-ql-stat). The per-tile change indicators
 * are gone: one arrow in the frame, on the score the view is named after. */

.mkt-vs-kpis {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  margin: 0;
  flex: 0 0 auto;
}
.mkt-vs-kpi {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  padding: 8px 10px 9px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #FFFFFF;
}
.mkt-vs-kpi dt {
  min-width: 0;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8.5px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mkt-vs-kpi dd {
  position: relative;
  display: flex;
  align-items: baseline;
  gap: 4px;
  margin: 0;
  min-width: 0;
}
.mkt-vs-value { font-size: 15px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.15; color: var(--ink); }
.mkt-vs-unit { margin-left: -2px; font-size: 11px; font-weight: 500; color: #4C5163; }
.mkt-vs .mkt-delta { white-space: nowrap; }

/* The loader the app shows while a stat is still coming in: visible only while the run is
   going, so the settled frame (and a frame without animation) never shows it. */
.mkt-vs-skel {
  position: absolute;
  left: 0;
  top: 50%;
  translate: 0 -50%;
  width: 72%;
  height: 11px;
  border-radius: 3px;
  background: linear-gradient(90deg, #ECEEF3 0%, #F7F8FB 50%, #ECEEF3 100%);
  background-size: 220% 100%;
  opacity: 0;
  pointer-events: none;
}

/* --- the mentions chart --------------------------------------------------------- */

.mkt-vs-chart { display: flex; flex-direction: column; gap: 8px; flex: 1 1 auto; min-width: 0; min-height: 0; }
/* Only the plot gives way when the frame is short: a caption, a day row or a key that shrinks
   clips its own text. */
.mkt-vs-chart > .mkt-vs-cap,
.mkt-vs-days,
.mkt-vs-keys { flex: 0 0 auto; }
.mkt-vs-plot { position: relative; flex: 1 1 auto; min-height: 64px; padding-left: var(--gut, 22px); }

/* Grid lines are HTML: the bars stretch with the plot, the numbers stay crisp. */
.mkt-vs-rule {
  position: absolute;
  left: var(--gut, 22px);
  right: 0;
  bottom: calc(var(--v) / var(--max) * 100%);
  border-top: 1px solid var(--line);
}
.mkt-vs-rule.is-axis { border-top-color: #D9DCE5; }
.mkt-vs-rule b {
  position: absolute;
  right: calc(100% + 6px);
  top: 0;
  translate: 0 -50%;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8.5px;
  font-weight: 400;
  color: #9AA0B4;
}
/* The bars are stretched into this box; a replaced element cannot be sized by insets alone
   (width/height: auto would fall back to the SVG's intrinsic 300×300). */
.mkt-vs-canvas { position: absolute; inset: 0 0 0 var(--gut, 22px); }
.mkt-vs-bars { display: block; width: 100%; height: 100%; }
.mkt-vs-col { transform-box: fill-box; transform-origin: 50% 100%; }

.mkt-vs-days {
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  padding-left: var(--gut, 22px);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8.5px;
  color: #9AA0B4;
}
.mkt-vs-days span { text-align: center; }

/* --- the chart's key ------------------------------------------------------------
 * The legend the app's chart carries, in the compact form its own narrow-viewport preset uses
 * (common-apex-options.ts: under the chart, left-aligned, 11px text, 5px markers). It names the
 * nine models the run covered, so it stays readable text rather than decoration. Nine keys do
 * not fit one line, and a free wrap left two of them alone on the second (7 + 2 at 1440), so the
 * key is a grid of five columns: 5 + 4, the brick the model logos make everywhere else on the
 * site, with every column as wide as its longest name. The phone column below takes 3 x 3. */

.mkt-vs-keys { display: grid; grid-template-columns: repeat(5, max-content); gap: 4px 13px; }
.mkt-vs-key {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
  font-size: 11px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mkt-vs-key::before {
  content: "";
  width: 7px;
  height: 7px;
  flex: 0 0 auto;
  border-radius: 2px;
  background: var(--c);
}

/* Wide previews (the single-column 768 layout and the home stage): the full metric title, a
   bigger gauge and more air between every band. */
@container (min-width: 620px) {
  .mkt-vs-stage { gap: 14px; }
  .mkt-vs-cap { font-size: 9.5px; }
  .mkt-vs-cap.is-long { display: block; }
  .mkt-vs-cap.is-short { display: none; }
  .mkt-vs-ring { width: 54px; height: 54px; }
  .mkt-vs-ring .mkt-vs-num { font-size: 19px; }
  .mkt-vs .mkt-vs-score-copy .mkt-delta { font-size: 12.5px; }
  .mkt-vs-steps { font-size: 13px; }
  .mkt-vs-kpis { gap: 12px; }
  .mkt-vs-kpi { padding: 10px 13px 11px; border-radius: 12px; gap: 5px; }
  .mkt-vs-kpi dt { font-size: 9.5px; }
  .mkt-vs-value { font-size: 19px; }
  .mkt-vs-unit { font-size: 13px; }
  .mkt-vs-skel { height: 14px; }
  .mkt-vs-chart { gap: 10px; }
  .mkt-vs-days, .mkt-vs-rule b { font-size: 9.5px; }
  .mkt-vs-plot { --gut: 26px; }
  .mkt-vs-keys { gap: 5px 20px; }
  .mkt-vs-key { font-size: 11.5px; gap: 7px; }
  .mkt-vs-key::before { width: 8px; height: 8px; }
}

/* Short previews (the 1024 column is ~456 px wide and only ~300 px tall): the same three bands,
   each trimmed so the chart still keeps the height it needs. */
@container (max-width: 470px) {
  .mkt-vs-stage { gap: 9px; }
  .mkt-vs-top { gap: 10px; }
  .mkt-vs-ring { width: 40px; height: 40px; }
  .mkt-vs-ring .mkt-vs-num { font-size: 14px; }
  .mkt-vs .mkt-vs-score-copy .mkt-delta { font-size: 10.5px; }
  .mkt-vs-kpis { gap: 6px; }
  .mkt-vs-kpi { padding: 6px 8px 7px; }
  .mkt-vs-value { font-size: 14px; }
  .mkt-vs-keys { gap: 4px 11px; }
}

/* Narrow previews (the phone column): the filter chips go, the head keeps the score on its own
   line and the chart's key wraps onto three columns. */
@container (max-width: 430px) {
  .mkt-vs-stage { gap: 8px; }
  .mkt-vs-filters { display: none; }
  .mkt-vs-steps { font-size: 11.5px; }
  .mkt-vs-ring { width: 38px; height: 38px; }
  .mkt-vs-ring .mkt-vs-num { font-size: 13.5px; }
  .mkt-vs-kpis { gap: 5px; }
  .mkt-vs-kpi { padding: 5px 7px 6px; border-radius: 9px; gap: 2px; }
  /* "ŚREDNIA POZYCJA" is the longest of the three labels and clipped at 360 px — the narrowest
     phone still in use — so the tracking comes off before the size does. */
  .mkt-vs-kpi dt { font-size: 8px; letter-spacing: 0.01em; }
  .mkt-vs-value { font-size: 13px; }
  .mkt-vs-unit { font-size: 10px; }
  .mkt-vs-skel { height: 9px; }
  .mkt-vs-chart { gap: 6px; }
  .mkt-vs-plot { --gut: 19px; min-height: 48px; }
  .mkt-vs-days { font-size: 8px; }
  .mkt-vs-keys { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 4px 8px; }
  .mkt-vs-key { font-size: 9.5px; gap: 4px; }
  .mkt-vs-key::before { width: 6px; height: 6px; }
}

/* --- the run -------------------------------------------------------------------
 * Every delay is written out: a duration behind var() does not survive being seeked, which is
 * how these frames are checked. */

/* 0–0.40 s — the view sets itself up: filters, axis, the chart's key, the tiles with their
   loaders. */
.mkt-vs.is-armed .mkt-vs-chip { animation: vs-rise 0.3s ease calc(0.04s + var(--i) * 0.06s) backwards; }
.mkt-vs.is-armed .mkt-vs-rule { animation: vs-fade 0.28s ease calc(var(--i) * 0.035s) backwards; }
.mkt-vs.is-armed .mkt-vs-rule b { animation: vs-fade 0.28s ease calc(0.04s + var(--i) * 0.035s) backwards; }
.mkt-vs.is-armed .mkt-vs-chart > .mkt-vs-cap { animation: vs-rise 0.28s ease 0.04s backwards; }
.mkt-vs.is-armed .mkt-vs-key { animation: vs-rise 0.28s ease calc(0.07s + var(--k) * 0.045s) backwards; }
.mkt-vs.is-armed .mkt-vs-kpi { animation: vs-rise 0.3s ease calc(0.04s + var(--j) * 0.06s) backwards; }

/* 0.10–1.05 s — the mentions chart fills day by day and its total counts with it. */
.mkt-vs.is-armed .mkt-vs-col { animation: vs-col 0.42s cubic-bezier(0.22, 1, 0.36, 1) calc(0.10s + var(--i) * 0.075s) backwards; }
.mkt-vs.is-armed .mkt-vs-days span { animation: vs-rise 0.28s ease calc(0.15s + var(--i) * 0.075s) backwards; }
/* Two passes outlive the longest loader (2 × 0.9s = 1.8s): an `infinite` shimmer would keep
   repainting a background nobody can see for as long as the page is open. */
.mkt-vs.is-armed .mkt-vs-kpi.is-mentions .mkt-vs-skel { animation: vs-skel 0.32s linear both, vs-shimmer 0.9s ease-in-out 2; }
.mkt-vs.is-armed .mkt-vs-kpi.is-mentions .mkt-vs-num { animation: vs-count 0.73s linear 0.32s both, vs-fade 0.22s ease 0.32s backwards; }

/* 0.80–1.30 s — the answers are counted: the share and the position tiles settle. */
.mkt-vs.is-armed .mkt-vs-kpi.is-sov .mkt-vs-skel { animation: vs-skel 0.8s linear both, vs-shimmer 0.9s ease-in-out 2; }
.mkt-vs.is-armed .mkt-vs-kpi.is-sov .mkt-vs-unit { animation: vs-fade 0.22s ease 0.8s backwards; }
/* One declaration: the value is also the counter, and a second animation rule on the same element
   would replace this one instead of adding to it (the tile flashed "0,0" under the loader). */
.mkt-vs.is-armed .mkt-vs-kpi.is-sov .mkt-vs-num { animation: vs-count 0.5s linear 0.8s both, vs-fade 0.22s ease 0.8s backwards; }
.mkt-vs.is-armed .mkt-vs-kpi.is-position .mkt-vs-skel { animation: vs-skel 1s linear both, vs-shimmer 0.9s ease-in-out 2; }
.mkt-vs.is-armed .mkt-vs-kpi.is-position .mkt-vs-value { animation: vs-rise 0.28s ease 1s backwards; }

/* 0–1.40 s — the status line. ONE line, held for its whole run and then carried off with the
   rest of the row: no second sentence flickering past in half a second, and no "done" beat —
   the score landing in the same slot is the only "done" the frame needs. Two turns of the
   spinner cover exactly the 1.4 s the row is up (2 × 0.7s). */
.mkt-vs.is-armed .mkt-vs-run { animation: vs-run 1.4s linear both; }
.mkt-vs.is-armed .mkt-vs-spinner { animation: vs-spin 0.7s linear 2, vs-fade 0.3s ease both; }
.mkt-vs.is-armed .mkt-vs-steps > .is-collect { animation: vs-rise 0.3s ease both; }

/* 1.40–2.10 s — the head is handed over: the row is at opacity 0 exactly as the gauge starts to
   fade in (a gauge fading in through a line of text is a mess, so the two never share the frame),
   then the gauge sweeps into the slot and closes on 72. The halo and the ↑ 4 land with it, so the
   frame is settled by 2.35 s — inside the 3 s the home page's tab rotation allows. */
.mkt-vs.is-armed .mkt-vs-score { animation: vs-fade 0.3s ease 1.4s backwards; }
.mkt-vs.is-armed .mkt-vs-ring .is-value { animation: vs-ring 0.7s cubic-bezier(0.22, 1, 0.36, 1) 1.4s both; }
.mkt-vs.is-armed .mkt-vs-ring .mkt-vs-num { animation: vs-count 0.7s cubic-bezier(0.22, 1, 0.36, 1) 1.4s both; }
.mkt-vs.is-armed .mkt-vs-halo { animation: vs-halo 0.7s ease-out 2s 1; }
.mkt-vs.is-armed .mkt-vs-score-copy .mkt-delta { animation: vs-pop 0.3s cubic-bezier(0.2, 0.9, 0.3, 1.3) 2.05s backwards; }
.mkt-vs.is-armed .mkt-vs-score-copy .mkt-vs-cap { animation: vs-fade 0.3s ease 1.47s backwards; }

/* Waiting for marketing.js to mark the widget live: hold the first frame. */
.mkt-widget:not(.is-live) .mkt-vs.is-armed *,
.mkt-widget:not(.is-live) .mkt-vs.is-armed *::after,
.mkt-widget:not(.is-live) .mkt-vs.is-armed *::before { animation-play-state: paused !important; }

@media (prefers-reduced-motion: reduce) {
  .mkt-vs *,
  .mkt-vs *::after,
  .mkt-vs *::before { animation: none !important; }
}

@keyframes vs-fade { from { opacity: 0; } }
@keyframes vs-rise { from { opacity: 0; translate: 0 4px; } }
@keyframes vs-pop { from { opacity: 0; scale: 0.7; } }
@keyframes vs-col { from { transform: scaleY(0); } }
@keyframes vs-ring { from { stroke-dasharray: 0 100; } }
@keyframes vs-count { from { --vs-n: 0; --vs-d: 0; } }
@keyframes vs-halo { from { opacity: 0.85; scale: 0.9; } to { opacity: 0; scale: 1.12; } }
@keyframes vs-spin { to { rotate: 360deg; } }
@keyframes vs-run { 0%, 91% { opacity: 1; } 100% { opacity: 0; } }
@keyframes vs-skel { 0%, 86% { opacity: 1; } 100% { opacity: 0; } }
@keyframes vs-shimmer { from { background-position: 130% 0; } to { background-position: -130% 0; } }

/* === wizard-brand ========================================================== */
/*
 * Product page preview — onboarding step 1, "Wybór marki" (widgets/v2/wizard-brand.blade.php).
 *
 * Every rule below describes the FINISHED step: the query typed, the four Nike Inc. brands
 * found, Nike picked, and its details open under "Dostosuj". The sequence that leads there is
 * a set of keyframes whose early frames hold the earlier states. They apply only once the
 * widget is armed by its inline script, sit paused until marketing.js marks the widget live,
 * and restart by themselves each time the accordion shows the widget again (display: none
 * cancels CSS animations).
 *
 * Timeline (s): 0–0.29 type "Nike" · 0.36 search pressed · 0.38 the status opens with the
 *               spinner · 0.44–0.62 the four result rows land as skeletons · 0.90–1.14 they
 *               fill in with the real brands · 1.30 tick, "Znaleziono 4 marki" · 1.60 Nike
 *               picked · 1.74–2.06 the status clears itself away · 2.04 "Dostosuj" pressed
 *               · 2.20–2.66 the panel opens, its rows already composed inside the clip
 *               · 2.36–2.76 the three other results take up whatever slack is left, so the
 *                 list finishes flush with the bottom of the card instead of over a dead band.
 *
 * The status is NOT a badge the step then wears. It is a disclosure row whose base value is
 * `grid-template-rows: 0fr`, opened and closed again by the sequence, so the settled frame —
 * which is also the no-JS frame and the reduced-motion frame — carries no status text, no
 * green tick and no found-counter, and no empty band where they used to be: the row has no
 * height to leave behind. The list of results underneath is what states what was found.
 *
 * Nothing before its cause: every keyframe that is backwards-filled through a delay holds the
 * HIDDEN state at 0%, so the opening frame is an empty search box — no spinner and no status
 * sitting there before the query is typed.
 *
 * Class prefix mkt-wb-. Shared classes (mkt-wiz-step*, mkt-brand-mark) are only ever
 * overridden under .mkt-wb so the live page and the home page keep their own styling.
 */

/* marketing.css sets `.mkt-widget-body.mkt-wiz { gap: 14px }` and `.mkt-how-preview .mkt-wiz
   { gap: 10px }` — both two-class selectors, so the gap has to be set at the same
   specificity here or the widget keeps the shared 14px and overflows the short previews. */
.mkt-widget-body.mkt-wb {
  container-type: inline-size;
  gap: 0;
}
/* The body is the container, and a container query can only style its DESCENDANTS — never the
   container itself. So the space between the body's rows is set as a margin on the rows, which
   the queries below can change per preview size; a `gap` here would be stuck at one value. */
.mkt-wb-search,
.mkt-wb-hits { margin-top: 9px; }

/* The stepper is shared with the live widget; only its scale changes here. */
.mkt-wb .mkt-wiz-steps { gap: 4px; }
.mkt-wb .mkt-wiz-steps::before { top: 9px; }
.mkt-wb .mkt-wiz-step { gap: 3px; }
.mkt-wb .mkt-wiz-step-dot { width: 18px; height: 18px; font-size: 9px; }
.mkt-wb .mkt-wiz-step-label { font-size: 8px; }

/* --- search ------------------------------------------------------------------ */

.mkt-wb-search {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  min-height: 32px;
  padding: 4px 5px 4px 10px;
  border: 1px solid #DEE0E8;
  border-radius: 10px;
  background: #FFFFFF;
}
.mkt-wb-search-ico { display: flex; flex: 0 0 auto; color: #8B90A3; }
.mkt-wb-field {
  flex: 1;
  min-width: 0;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.2;
  color: #14162B;
  white-space: nowrap;
}
/* One box per character so the caret sits exactly after the last one typed. */
.mkt-wb-typed > span {
  display: inline-block;
  max-width: 1.4em;
  overflow: hidden;
  vertical-align: bottom;
}
.mkt-wb-caret {
  display: inline-block;
  width: 1.5px;
  height: 12px;
  margin-left: 1px;
  vertical-align: -1px;
  background: #5B5CE6;
  opacity: 0;
}
.mkt-wb-go {
  flex: 0 0 auto;
  padding: 5px 10px;
  border-radius: 8px;
  background: #5B5CE6;
  color: #FFFFFF;
  font-size: 11px;
  font-weight: 600;
}

/* --- no status line ------------------------------------------------------------ */

/* There is none. The rows below load as grey bars and fill in when the brands come back — the
   lookup already has its loading state, in the place the app puts it. A "Szukam marek…" line
   above them repeated that, and it had to open a row to do it and close it again afterwards,
   which dropped the list 24px and lifted it back for nothing. How many were found is what the
   list states. */
/* --- results ------------------------------------------------------------------ */

.mkt-wb-hits {
  display: flex;
  flex-direction: column;
  /* Top-anchored: the results appear under the search box the way the real list loads, and the
     "Dostosuj" panel then grows downwards instead of shoving the whole list upwards. */
  justify-content: flex-start;
  gap: 4px;
  flex: 1 1 auto;
  min-height: 0;
}
.mkt-wb-hit {
  display: flex;
  flex-direction: column;
  border: 1px solid #EDEEF2;
  border-radius: 10px;
  background: #FFFFFF;
}
/* The preview box is a fixed 5:4, so at most widths it is taller than the four rows and the
   open panel need. That leftover goes into the three plain rows — air spread down the list,
   the way the sibling tabs' own rows share their box (grid-auto-rows: 1fr) — instead of
   pooling into one dead band under the last result. The picked row keeps its own height: it
   is the one the panel grows inside.
   0.34, not 1: flex hands out the whole leftover as soon as the factors sum to 1, so three
   rows at 1 would jump to full height in the first frames of the animation below. Summing to
   1.02 means the same finished layout, reached across the whole opening. */
.mkt-wb-hit:not(.is-on) { flex: 0.34 1 auto; }
.mkt-wb-hit.is-on {
  border-color: #5B5CE6;
  background: #F7F8FE;
  box-shadow: inset 0 0 0 1px #5B5CE6, 0 10px 22px -18px rgba(20, 22, 43, 0.55);
}
/* Room reclaimed from the counter and the cleared status line goes here, as air around the
   four results, rather than into more rows. Paid four times over, so it is measured, not felt.
   flex: 1 1 auto so that when a row is handed slack by the rule above, the slack lands here
   and the mark and the name stay centred in the taller row rather than clinging to its top. */
.mkt-wb-head { display: flex; align-items: center; flex: 1 1 auto; gap: 9px; padding: 3px 7px; }
.mkt-wb .mkt-brand-mark { width: 26px; height: 26px; border-radius: 8px; }
.mkt-wb-copy { position: relative; display: flex; flex-direction: column; gap: 0; flex: 1; min-width: 0; }
.mkt-wb-copy strong {
  font-size: 12px;
  font-weight: 600;
  line-height: 1.35;
  color: #14162B;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mkt-wb-copy > span {
  font-size: 10px;
  line-height: 1.35;
  color: #656B7E;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Loading bars: they sit over the row's copy while the lookup runs, and are gone by the
   finished frame (so no-JS and reduced motion never show them). The element is an <i>, and
   has to stay one: as a <span> it is a direct span child of .mkt-wb-copy, which is what the
   row-text animation at the bottom of this block selects — and that selector, one element
   heavier, silently took the skeleton's own animation away and left four blank rows. */
.mkt-wb-skel {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  opacity: 0;
}
.mkt-wb-skel b { height: 5px; border-radius: 3px; background: #E7E9F0; }
.mkt-wb-skel b:first-child { width: 38%; background: #DDE0EA; }
.mkt-wb-skel b:last-child { width: 62%; }
.mkt-wb-check {
  position: relative;
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  border-radius: 50%;
  background: #5B5CE6;
}
.mkt-wb-check::after {
  content: "";
  position: absolute;
  left: 4.9px;
  top: 3.2px;
  width: 3px;
  height: 5.8px;
  border: solid #FFFFFF;
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}
.mkt-wb-btn {
  flex: 0 0 auto;
  padding: 4px 8px;
  border-radius: 7px;
  font-size: 10px;
  font-weight: 600;
  white-space: nowrap;
}
.mkt-wb-btn.is-ghost { border: 1px solid #D6DAFB; background: #FFFFFF; color: #4B4CD8; }
.mkt-wb-btn.is-primary { background: #5B5CE6; color: #FFFFFF; }

/* --- the details panel "Dostosuj" opens --------------------------------------- */

.mkt-wb-adjust { display: grid; grid-template-rows: 1fr; overflow: hidden; }
/* Same two boxes and the same reasons as the status row above: start-aligned and empty so the
   track can measure it and close on it, with the padding one level further in. A stretched
   item in a part-open `<n>fr` track resolves shorter than the track, which is what used to
   open a band of bare tinted row under the half-revealed card for the length of the opening. */
.mkt-wb-adjust-clip { align-self: start; min-height: 0; }
.mkt-wb-adjust-in { padding: 1px 7px 5px; }
/* The app opens straight into the rows inside a bordered Paper: there is no panel title there
   and none here either. The "Dostosuj" button directly above already names the panel, and a
   mono uppercase title sitting on top of a card of mono uppercase labels was a large part of
   what made this the least readable corner of the widget. */
.mkt-wb-card {
  display: flex;
  flex-direction: column;
  padding: 2px 10px 5px;
  border: 1px solid #E4E6F3;
  border-radius: 9px;
  background: #FFFFFF;
}
.mkt-wb-fields { margin: 0; }
/* brand-details-layout.tsx: a fixed label column beside the value, one divider per row.
   These base values are what the 558px container gets — the 1440 and 1920 previews, see the
   band map above the container queries below. 128px, not less: at 112px "Aliasy i alternatywne
   nazwy" broke into three lines and became the tallest row in the panel. */
.mkt-wb-row {
  display: grid;
  grid-template-columns: 128px minmax(0, 1fr);
  column-gap: 10px;
  align-items: center;
  min-width: 0;
  padding: 3px 0;
  border-top: 1px solid #F3F4F8;
}
.mkt-wb-row:first-child { border-top: 0; }
.mkt-wb-row.is-lvl1,
.mkt-wb-row.is-lvl2 { display: none; }
/* brandDetailsLabelSx — plain semibold text in the secondary colour, sentence case. The mono
   uppercase micro-label this used to be was unreadable by 8px, competed with the value beside
   it, and is not what the app shows. One type family now runs down the whole panel: label,
   value and link differ by weight and colour, never by typeface. */
.mkt-wb-fields dt {
  font-size: 9.5px;
  font-weight: 600;
  line-height: 1.35;
  color: #7C8196;
  min-width: 0;
  overflow-wrap: break-word;
}
.mkt-wb-fields dd {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  min-width: 0;
}
.mkt-wb-val {
  min-width: 0;
  font-size: 11px;
  line-height: 1.45;
  color: #262A47;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mkt-wb-val.is-strong { font-weight: 600; color: #14162B; }
/* The website is a Link in the app's panel; the brand colour carries that on its own. */
.mkt-wb-val.is-link { color: #4B4CD8; }
/* ONE chip style, used by both chip rows: the same pill geometry every other widget's chips
   use (2px 8px, 999px, #E3E5EC border), in the page's own type because these carry catalog
   phrases ("Obuwie i wyroby skórzane") rather than one-word filter labels. They wrap rather
   than being cut — half a chip disappearing at the column edge reads as a bug. */
.mkt-wb-chips { display: flex; flex-wrap: wrap; gap: 4px; row-gap: 3px; min-width: 0; }
.mkt-wb-chip {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border: 1px solid #E3E5EC;
  border-radius: 999px;
  background: #FFFFFF;
  font-size: 9.5px;
  line-height: 1.4;
  color: #4C5163;
  white-space: nowrap;
}
.mkt-wb-foot { display: flex; justify-content: flex-end; padding-top: 5px; }

/* The panel grows with the preview instead of squeezing into it: the two fields that identify
   the brand always, the aliases once there is room, the industries once there is room for both
   chip rows to breathe. Two readable rows beat four squeezed ones.
   These widths are a stand-in for HEIGHT. The preview box is a fixed 5:4, so a container this
   wide is a box that tall, and each extra row has to be paid for out of the same box that has
   to hold four results. At 380 and 440 it was not: between container 381 and 398 — the
   970-1000px viewport band, where the preview column is at its narrowest before the page's
   0.9 zoom widens it again — the third row pushed the fourth result (Converse) out through
   the bottom of the card, and at 453 the fourth row overflowed by a hair. Measured. */
@container (min-width: 420px) {
  .mkt-wb-row.is-lvl1 { display: grid; }
}

@container (min-width: 460px) {
  .mkt-wb-row.is-lvl2 { display: grid; }
}

/* The widest preview (the 768 column): every step of the type scale a notch up, and real air
   between the rows rather than more of them. */
@container (min-width: 600px) {
  .mkt-wb .mkt-brand-mark { width: 32px; height: 32px; }
  .mkt-wb .mkt-wiz-step-dot { width: 20px; height: 20px; font-size: 9.5px; }
  .mkt-wb-head { padding: 6px 9px; gap: 11px; }
  .mkt-wb-copy strong { font-size: 13.5px; }
  .mkt-wb-copy > span { font-size: 11.5px; }
  .mkt-wb-search { min-height: 36px; }
  .mkt-wb-field { font-size: 13.5px; }
  .mkt-wb-card { padding: 3px 12px 6px; }
  /* Wide enough that "Aliasy i alternatywne nazwy" — the longest label the app has for this
     panel — sets on two lines, never three. Measured, not guessed. */
  .mkt-wb-row { grid-template-columns: 150px minmax(0, 1fr); padding: 5px 0; }
  .mkt-wb-fields dt { font-size: 10.5px; }
  .mkt-wb-val { font-size: 12px; }
  .mkt-wb-chip { font-size: 10.5px; }
  .mkt-wb-foot { padding-top: 6px; }
  .mkt-wb-skel b { height: 6px; }
}

/* THE BAND MAP. marketing.css puts `zoom: 0.9` on the body above a 1000px viewport, and a
   container query is evaluated in LAYOUT pixels while getBoundingClientRect reports the zoomed
   visual ones — so the container is always wider than a screenshot makes it look, and the
   bands have to be written against the layout width. Measured content-box widths:
       viewport 390 → 320   (phone band below, plus this one)
       viewport 1024 → 467  (this band)
       viewport 1440/1920 → 558  (NO band: the base sizes at the top of this block are written
                                  for it — that is why they are the roomiest of the small ones)
       viewport 768 → 662   (the 600px band above)
   Short previews: smaller rows, so four results and the open panel still fit with air left. */
@container (max-width: 520px) {
  .mkt-wb-search,
  .mkt-wb-hits { margin-top: 6px; }
  .mkt-wb .mkt-wiz-step-dot { width: 16px; height: 16px; font-size: 8.5px; }
  .mkt-wb .mkt-wiz-steps::before { top: 8px; }
  .mkt-wb .mkt-wiz-step-label { font-size: 7.5px; }
  .mkt-wb-search { min-height: 29px; padding: 3px 4px 3px 9px; }
  .mkt-wb-field { font-size: 11.5px; }
  .mkt-wb-hits { gap: 3px; }
  .mkt-wb-head { padding: 2px 6px; gap: 8px; }
  .mkt-wb .mkt-brand-mark { width: 23px; height: 23px; border-radius: 7px; }
  .mkt-wb-copy strong { font-size: 11px; line-height: 1.3; }
  .mkt-wb-copy > span { font-size: 9px; line-height: 1.3; }
  .mkt-wb-btn { padding: 3px 7px; font-size: 9.5px; }
  .mkt-wb-adjust-in { padding: 0 5px 3px; }
  .mkt-wb-card { padding: 1px 8px 3px; }
  /* 118px, not less: at 104px the alias label broke into three lines and became the tallest
     row in the panel. */
  .mkt-wb-row { grid-template-columns: 118px minmax(0, 1fr); column-gap: 8px; padding: 2px 0; }
  .mkt-wb-fields dt { font-size: 9px; }
  .mkt-wb-val { font-size: 10.5px; }
  .mkt-wb-chip { font-size: 9px; padding: 1px 7px; }
  .mkt-wb-foot { padding-top: 3px; }
  .mkt-wb-skel b { height: 4.5px; }
}

/* True phone width only: the stepper drops to its numbered dots, the way marketing.css drops
   them for any compact widget (.mkt-widget.is-compact .mkt-wiz-step-label). The sibling tabs
   of this step never need it — they carry one list each, where this one carries a search box,
   four results AND an open panel in a 350px square — so this is the one width where the three
   tabs' steppers differ, and it buys the fourth result its bottom gutter. */
@container (max-width: 380px) {
  .mkt-wb .mkt-wiz-step-label { display: none; }
}

/* The shortest previews: the panel is down to the two rows that identify the brand, and every
   row is on its tightest metrics. Everything here is paid four times over — the four result
   rows — so the fourth result still has to clear the bottom of a 350px-square preview. The
   band reaches to 400 rather than 380 because a 5:4 box 400 wide is only 320 tall: the same
   cramped preview, whatever side of 380 it falls on. */
@container (max-width: 400px) {
  .mkt-wb-search,
  .mkt-wb-hits { margin-top: 6px; }
  .mkt-wb-go { padding: 4px 9px; }
  .mkt-wb-head { padding: 1px 6px; }
  .mkt-wb-hits { gap: 2px; }
  /* 112px, not 88: at 88 "Strona internetowa" broke over two lines while its value sat on one,
     which is exactly the mush the panel was reworked to get rid of. The two rows that survive at
     this width carry short values ("Nike", "nike.com"), so the column can afford the width. */
  .mkt-wb-row { grid-template-columns: 112px minmax(0, 1fr); column-gap: 7px; padding: 2px 0; }
  .mkt-wb-fields dt { font-size: 8.5px; }
  .mkt-wb-val { font-size: 10px; }
}

/* --- sequence ----------------------------------------------------------------- */

.mkt-wb.is-armed .mkt-wb-typed > span { animation: wb-type 0.01s steps(1) calc(0.05s + var(--c) * 0.08s) backwards; }
.mkt-wb.is-armed .mkt-wb-caret { animation: wb-caret 0.36s steps(1) 0s 2; }
.mkt-wb.is-armed .mkt-wb-go { animation: wb-press 0.22s ease 0.36s backwards; }


/* The rows land as skeletons and fill in a moment later — the list loading, as in the app. */
.mkt-wb.is-armed .mkt-wb-hit { animation: wb-hit-in 0.3s cubic-bezier(0.22, 1, 0.36, 1) calc(0.44s + var(--i) * 0.06s) both; }
/* The list loads at the height its rows actually are, with the slack still under it, and takes
   that slack up on the tail of the opening — one settle, not a list that arrives stretched and
   then shrinks back. It trails the panel by 0.16 s on purpose: started together, the rows would
   chase a slack the panel is still eating and bulge by a row's worth before settling back.
   Backwards-filled from 0, so the finished frame, the no-JS frame and the reduced-motion frame
   all use the base factor above and fill the box. */
.mkt-wb.is-armed .mkt-wb-hit:not(.is-on) {
  animation:
    wb-hit-in 0.3s cubic-bezier(0.22, 1, 0.36, 1) calc(0.44s + var(--i) * 0.06s) both,
    wb-fill 0.4s cubic-bezier(0.22, 1, 0.36, 1) 2.36s backwards;
}
.mkt-wb.is-armed .mkt-wb-skel { animation: wb-skel 0.58s ease calc(0.44s + var(--i) * 0.06s) both; }
.mkt-wb.is-armed .mkt-brand-mark { animation: wb-mark 0.34s ease calc(0.88s + var(--i) * 0.08s) backwards; }
.mkt-wb.is-armed .mkt-brand-mark > * { animation: wb-fade 0.26s ease calc(0.94s + var(--i) * 0.08s) backwards; }
.mkt-wb.is-armed .mkt-wb-copy strong,
.mkt-wb.is-armed .mkt-wb-copy > span { animation: wb-rise 0.3s ease calc(0.9s + var(--i) * 0.08s) backwards; }
.mkt-wb.is-armed .mkt-wb-hit.is-on {
  animation:
    wb-hit-in 0.3s cubic-bezier(0.22, 1, 0.36, 1) 0.44s both,
    wb-pick 0.35s ease 1.6s backwards;
}
.mkt-wb.is-armed .mkt-wb-check { animation: wb-pop 0.32s cubic-bezier(0.2, 0.9, 0.3, 1.35) 1.64s backwards; }
.mkt-wb.is-armed .mkt-wb-btn.is-ghost {
  animation:
    wb-rise 0.3s ease 0.94s backwards,
    wb-press 0.22s ease 2.04s backwards;
}

/* The disclosure IS the motion, and it is the only one: the card is composed and opaque inside
   the clip, and the clip uncovers it from the top, row after row, the way the app's accordion
   does. The rows used to fade in one by one AFTER the box had already reached full height, so a
   large empty bordered panel sat open for a quarter of a second and then filled from the top —
   that read as loading, not as opening. One motion, no holes. */
.mkt-wb.is-armed .mkt-wb-adjust { animation: wb-open 0.46s cubic-bezier(0.22, 1, 0.36, 1) 2.2s backwards; }
.mkt-wb.is-armed .mkt-wb-card { animation: wb-fade 0.16s ease 2.2s backwards; }

/* Waiting for marketing.js to mark the widget live: hold the first frame. */
.mkt-widget:not(.is-live) .mkt-wb.is-armed *,
.mkt-widget:not(.is-live) .mkt-wb.is-armed *::after,
.mkt-widget:not(.is-live) .mkt-wb.is-armed *::before { animation-play-state: paused !important; }

@media (prefers-reduced-motion: reduce) {
  .mkt-wb *,
  .mkt-wb *::after,
  .mkt-wb *::before { animation: none !important; }
}

@keyframes wb-type { from { max-width: 0; } }
@keyframes wb-caret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
@keyframes wb-press { 0% { transform: none; } 45% { transform: scale(0.93); filter: brightness(0.94); } 100% { transform: none; } }
@keyframes wb-pop { from { opacity: 0; scale: 0.7; } }
@keyframes wb-rise { from { opacity: 0; translate: 0 4px; } }
@keyframes wb-fade { from { opacity: 0; } }
@keyframes wb-hit-in { from { opacity: 0; translate: 0 8px; scale: 0.985; } }
@keyframes wb-skel { 0% { opacity: 0.55; } 30% { opacity: 1; } 72% { opacity: 1; } 100% { opacity: 0; } }
/* The tile is a grey placeholder until the brand's mark arrives with the row's data. */
@keyframes wb-mark { from { background: #ECEEF4; } }
@keyframes wb-pick { from { border-color: #EDEEF2; background: #FFFFFF; box-shadow: inset 0 0 0 1px transparent, 0 10px 22px -18px rgba(20, 22, 43, 0); } }
@keyframes wb-open { from { grid-template-rows: 0fr; } }
@keyframes wb-fill { from { flex-grow: 0; } }

/* === wizard-competitors ==================================================== */
/*
 * Product page preview — /produkt-claude-preview, step 1 "Onboarding" › "Konkurenci"
 * (widgets/v2/wizard-competitors.blade.php).
 *
 * Every rule below describes the finished step: ten suggested competitors, five pre-selected by
 * the lookup, one typed in by hand, six tracked. The sequence that leads there is a set of
 * keyframe animations whose "from" frames hold the earlier states. They apply only once the
 * widget is armed by its inline script, sit paused until marketing.js marks the widget live, and
 * restart by themselves each time the accordion shows the widget again (display: none cancels CSS
 * animations).
 *
 * Timeline (s): 0–1.26 the lookup runs and the names resolve into their cells · 1.3–2.1 the five
 * most cited are pre-selected and the select-all button goes indeterminate · 2.15–2.30 the add
 * tile is pressed and lifts off the cell it was covering, which is now your row, ticked and
 * ringed · 2.55–3.25 "Under Armour" is typed into it · 3.36 the row is committed, the spinner
 * becomes a tick, the bar completes and the list is ready · 3.34–3.42 the fresh tile the wizard
 * appends behind the row resolves out of its own loading row.
 *
 * The tile and the grid under it are the "Zapytania" step's, deliberately: the two steps are read
 * one after the other in the same preview box, so the control that adds a row has to be the same
 * control in both. Two columns, the tile's face on the cell it turns into, and a fresh tile that
 * arrives once the row is counted — all of it is that step's, down to the 0.08s between the
 * tile's three arrival beats.
 *
 * The status line hands over at three fixed instants — 1.26, 2.10 and 3.36 s. The three lines of
 * the running process share one 3.6 s track so those instants are literal percentages
 * (1% = 36 ms), and the outgoing line is back at zero opacity before the incoming one starts: two
 * texts sharing that grid cell must never be painted at the same time.
 *
 * The fourth line is not a badge the step then wears for good. It arrives with the tick at
 * 3.36 s, holds for a beat and clears, leaving the line to the one number the step is about: the
 * competitors chosen. So the settled frame — which is also the no-JS and reduced-motion frame —
 * carries neither a status text nor a tick, only that count; the row keeps its height either way.
 */

@property --wc-n { syntax: '<integer>'; inherits: false; initial-value: 0; }
@property --wc-t { syntax: '<number>'; inherits: true; initial-value: 1; }

.mkt-widget-body.mkt-wc {
  gap: 8px;
  container-type: inline-size;
}

.mkt-wc-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.mkt-wc-num { --wc-n: var(--to); counter-reset: wc var(--wc-n); }
.mkt-wc-num::after { content: counter(wc); }

/* --- status line ------------------------------------------------------------- */

.mkt-wc-status { display: flex; align-items: center; gap: 9px; min-height: 18px; }
.mkt-wc-state { position: relative; width: 14px; height: 14px; flex: 0 0 auto; }
.mkt-wc-spinner {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid #E3E4FA;
  border-top-color: #5B5CE6;
  opacity: 0;
}
/* Hidden until the list lands, and hidden again once it has: the tick belongs to that moment. */
.mkt-wc-tick { position: absolute; inset: 0; border-radius: 50%; background: #22C55E; opacity: 0; }
.mkt-wc-tick::after {
  content: "";
  position: absolute;
  left: 4.7px;
  top: 3px;
  width: 3.3px;
  height: 6.2px;
  border: solid #FFFFFF;
  border-width: 0 1.6px 1.6px 0;
  transform: rotate(45deg);
}
/* 12px, not 12.5: the self-hosted Poppins 500 face opens visible gaps inside words at 12.5 and
   13px in Chromium ("List a konkurent ów got owa"), and it is the glyph advances that move, so
   letter-spacing cannot close them. 12px renders the same string cleanly. */
.mkt-wc-steps { display: grid; flex: 1; min-width: 0; font-size: 12px; font-weight: 500; color: #262A47; }
.mkt-wc-steps > span { grid-area: 1 / 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; opacity: 0; }

/* The end of the same line: the count of chosen competitors, ticking up with the boxes below it
   and staying there after the narration has cleared. */
.mkt-wc-selected { flex: 0 0 auto; font-size: 11.5px; color: #4C5163; white-space: nowrap; }
.mkt-wc-selected b { font-weight: 650; color: #14162B; }

.mkt-wc-bar { height: 3px; margin-top: -3px; border-radius: 3px; background: #EDEEF2; overflow: hidden; }
.mkt-wc-bar i { display: block; height: 100%; background: #5B5CE6; transform-origin: 0 50%; }

/* --- search bar ---------------------------------------------------------------
 * The app's SearchBar: a filled field whose label sits in place of a placeholder while it is
 * empty, the magnifier in the end adornment, then the two outlined action buttons. */

.mkt-wc-search {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  border: 1px solid #DEE0E8;
  background: #FFFFFF;
  border-radius: 9px;
  padding: 4px 4px 4px 10px;
}
.mkt-wc-search-field { flex: 1; min-width: 0; }
.mkt-wc-search-label {
  display: block;
  font-size: 12px;
  color: #8B90A3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mkt-wc-search-ico { display: flex; flex: 0 0 auto; margin-right: 2px; color: #A2A7B8; }
.mkt-wc-search-ico svg { width: 14px; height: 14px; }

.mkt-wc-act {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border: 1px solid #DEE0E8;
  border-radius: 7px;
  color: #656B7E;
  background: #FFFFFF;
}
.mkt-wc-act svg { width: 13px; height: 13px; }
/* The refresh button spins while the lookup is running, exactly as the app's loading state does. */
.mkt-wc-act-spin {
  position: absolute;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 1.8px solid #E3E4FA;
  border-top-color: #5B5CE6;
  opacity: 0;
}
/* Select-all is a tri-state checkbox; with five of ten picked it rests on indeterminate. */
.mkt-wc-act.is-all svg { position: absolute; }
.mkt-wc-act.is-all .is-empty { opacity: 0; }
.mkt-wc-act.is-all .is-some { opacity: 1; color: #5B5CE6; }

/* --- the wizard's type scale, shared with the "Zapytania" step ------------------
 * "Zapytania" and "Konkurenci" are two frames of one wizard, shown one after the other in the
 * same preview box, so they are read against each other. What sits inside a cell is therefore
 * sized once, here, for both grids: the prompts and the brand names, the query and the competitor
 * typed in by hand, and the label on the "+" tile in either step.
 *
 * The scale is the one the prompts need. They are the longest strings on either frame and the
 * only ones that wrap, and they set the floor: 11px on the narrow previews, because those are
 * drawn at zoom .9 and the self-hosted Poppins face drops the spaces between words below ~10.5px
 * of rendered size. The phone preview carries no zoom and is the one place 10.5px is set.
 *
 * Keeping it in one rule is the whole point. Split per widget it drifted twice: first the add
 * label rode this grid's own clamp and came out at 14px/650 against 12.5px/600 on the tile next
 * door, then the two labels were equalised to each other alone — which left the competitors'
 * label the smallest type in its grid (12.5 against 14px brand names on the 768 frame) while the
 * queries' label was the largest in its (12.5 against 12px prompts). Sizes that must agree are
 * one declaration; one declaration cannot disagree with itself.
 *
 * The thresholds are the "Zapytania" block's, 601 / 500 / 360, so neither step ever steps up a
 * size on a frame where the other does not. They are container CSS pixels, i.e. the widget body's
 * content box: ~662 at 768, ~558 on the desktop preview, ~467 at 1024, ~398 at 999 and ~320 on
 * the phone. */
.mkt-wc-grid,
.mkt-wq-grid { --wz-text: clamp(11px, 2.1cqw, 12px); }
@container (min-width: 601px) {
  .mkt-wc-grid,
  .mkt-wq-grid { --wz-text: 12px; }
}
@container (max-width: 500px) {
  .mkt-wc-grid,
  .mkt-wq-grid { --wz-text: 11px; }
}
@container (max-width: 360px) {
  .mkt-wc-grid,
  .mkt-wq-grid { --wz-text: 10.5px; }
}

/* The label on the "+" tile, in both steps: one tile, one rule — size, weight, colour and
 * letterfit together. It is set at the size of the items it sits among in its own grid, in both
 * steps, and what marks it as the control is the 600 and the lilac rather than a size of its own.
 * No word-spacing: the items beside it carry none, and a space opened up on one tile and not on
 * the other is precisely how the two labels came apart the last time. */
.mkt-wc-add-text,
.mkt-wc-face-text,
.mkt-wq-card.is-add .mkt-wq-text,
.mkt-wq-face-text {
  font-size: var(--wz-text);
  font-weight: 600;
  line-height: 1.22;
  color: #3D3FCF;
  word-spacing: normal;
}

/* --- the grid of competitors --------------------------------------------------
 * Ten suggestions, the hand-typed one, then the add tile: twelve cells, TWO columns, six rows
 * that share whatever height the preview has left — the shape the "Zapytania" step takes in the
 * same box, and the shape add-project-step-competitors.tsx itself lays its tiles out in.
 *
 * Two columns and not three because of what a cell has to hold. The three-column grid gave the
 * add tile 135-190px, and "Dodaj własnego konkurenta" needs 165 on one line, so that tile — and
 * only that tile — carried two lines of text in a grid where every other cell carried one, at
 * every width from 1015 up and from 535 to 725. The step before it, on the same frame, had its
 * own tile on one line. Two columns give the label 250-310px and the two steps break the same
 * way; the rows that this costs are rows the box has, because the "Zapytania" grid already runs
 * seven of them here where this one now runs six.
 *
 * The rows share that height, but a cell holding one word does not grow with it: the cards are
 * centred in their rows and capped at 3.4em, so anything the preview has over is spent as air
 * BETWEEN the cards instead of inflating them. A competitor tile then reads at the same weight
 * as a prompt card in the step before it, whatever box the preview hands the widget.
 *
 * The font-size below is the cards' metric basis and no longer their type: the checkbox, the
 * paddings, the gap, the placeholder bar and the 3.4em cap are all em of it, and the text inside
 * the cells takes --wz-text instead. The two used to be the same number, which is how the brand
 * names ended up two sizes above the prompts in the step before them — and the add tile between
 * them, at a third size again. */

.mkt-wc-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-auto-rows: 1fr;
  align-items: center;
  gap: 7px;
  flex: 1 1 auto;
  min-height: 0;
  font-size: clamp(11px, 2.35cqw, 14px);
}

.mkt-wc-card,
.mkt-wc-add {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.55em;
  min-width: 0;
  /* A cell's own box, written out once: the checkbox, the block padding either side of it and the
     1px border. Every suggestion cell is exactly this tall wherever the grid's row is shorter
     than it, and the add tile — whose "+" chip and 1.5px dashed stroke are not a checkbox and a
     1px border — is held to the same number by it in the narrow band below, instead of having a
     padding picked by eye. */
  --wc-cell-h: calc(1.3em + 2 * 0.4em + 2px);
  /* Never shorter than its contents, never taller than its row, and never taller than 3.4em. */
  min-height: min(100%, 3.4em);
  padding: 0.4em 0.62em;
  border: 1px solid #EDEEF2;
  border-radius: 9px;
  background: #FFFFFF;
  overflow: hidden;
}

.mkt-wc-card.is-on {
  border-color: #5B5CE6;
  background: #F7F8FE;
  box-shadow: inset 0 0 0 1px #5B5CE6;
}

.mkt-wc-check {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 1.3em;
  height: 1.3em;
  border: 1.5px solid #C4C8D6;
  border-radius: 0.36em;
  background: #FFFFFF;
  color: transparent;
}
.mkt-wc-check svg { width: 0.8em; height: 0.8em; }
.mkt-wc-card.is-on .mkt-wc-check { background: #5B5CE6; border-color: #5B5CE6; color: #FFFFFF; }

.mkt-wc-name {
  min-width: 0;
  font-size: var(--wz-text);
  color: #14162B;
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The placeholder bar that holds a cell's shape until its name arrives. Three widths in
   rotation, so the waiting grid reads as a list of results rather than a pattern. */
.mkt-wc-ghost {
  position: absolute;
  left: calc(0.62em + 1.3em + 0.55em);
  top: 50%;
  translate: 0 -50%;
  height: 0.6em;
  border-radius: 999px;
  background: #EDEEF2;
  opacity: 0;
  pointer-events: none;
}
/* Three widths in rotation over two columns, so no column ever repeats one. */
.mkt-wc-grid > .mkt-wc-card:nth-child(3n+1) .mkt-wc-ghost { right: 62%; }
.mkt-wc-grid > .mkt-wc-card:nth-child(3n+2) .mkt-wc-ghost { right: 55%; }
.mkt-wc-grid > .mkt-wc-card:nth-child(3n+3) .mkt-wc-ghost { right: 58%; }

/* The typed-in competitor: the app's inline text input, placeholder and all. */
.mkt-wc-field {
  position: relative;
  flex: 0 1 auto;
  width: max-content;
  max-width: 100%;
  min-width: 0;
  /* On the names' size, not the grid's: the placeholder, the typed name and the caret that walks
     along it are the same text as the cells around them. */
  font-size: var(--wz-text);
  line-height: 1.25;
  --wc-t: 1;
}
.mkt-wc-ph {
  position: absolute;
  left: 0;
  top: 50%;
  translate: 0 -50%;
  color: #A2A7B8;
  white-space: nowrap;
  opacity: 0;
}
.mkt-wc-typed {
  display: block;
  color: #14162B;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  clip-path: inset(-25% calc((1 - var(--wc-t)) * 100%) -25% 0);
}
.mkt-wc-caret {
  position: absolute;
  top: 50%;
  left: calc(var(--wc-t) * 100%);
  translate: 0 -50%;
  width: 1.4px;
  height: 1.05em;
  border-radius: 1px;
  background: #5B5CE6;
  opacity: 0;
}

.mkt-wc-add,
.mkt-wc-face {
  border: 1.5px dashed #5B5CE6;
  background: #EEF0FE;
}
/* The face that covers the cell you type into until it is pressed — the "Zapytania" step's
   .mkt-wq-face, built from this grid's own numbers. Inset -1px so it sits over the card's 1px
   border rather than inside it, which is what lets a 1.5px dashed stroke read as the tile's own
   edge. It rests invisible: the settled frame has the competitor in the cell, not the tile. */
.mkt-wc-face {
  position: absolute;
  inset: -1px;
  display: flex;
  align-items: center;
  gap: 0.55em;
  padding: 0 0.62em;
  border-radius: 9px;
  opacity: 0;
}
/* ...and the one cell in the grid that does NOT clip its own contents, because this face is the
   thing that would be clipped. A card clips at its padding box, and a face laid over the card's
   border box has its whole 1.5px dashed stroke outside that — 0.11px of it survives, which is
   enough to tint a lilac border underneath and not enough to draw a dash over a grey one. The
   cell has nothing else that could spill: the typed name is clipped by its own box, the
   placeholder is shorter than it, and the focus ring is drawn inwards. */
.mkt-wc-card.is-custom { overflow: visible; }
/* Its size, weight and colour come from the shared rule at the head of this block, with the
   "Zapytania" tile's; this is only what lets it wrap where that one wraps. */
.mkt-wc-face-text,
.mkt-wc-add-text {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  min-width: 0;
  text-wrap: balance;
  overflow: hidden;
}
/* The fresh tile's loading row: an opaque copy of a cell's geometry, so the grey box sits where
   a checkbox sits and the bar where a name sits. Same construction as .mkt-wq-skel, in this
   grid's em. */
.mkt-wc-skel {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: #FFFFFF;
  opacity: 0;
  pointer-events: none;
}
.mkt-wc-skel::before {
  content: "";
  position: absolute;
  left: 0.62em;
  top: 50%;
  translate: 0 -50%;
  width: 1.3em;
  height: 1.3em;
  border-radius: 0.36em;
  background: #EDEFF4;
}
.mkt-wc-skel::after {
  content: "";
  position: absolute;
  left: calc(0.62em + 1.3em + 0.55em);
  right: 30%;
  top: 50%;
  translate: 0 -50%;
  height: 0.6em;
  border-radius: 999px;
  background: #EDEEF2;
}
/* The "+" chip. Both onboarding steps show the same dashed add tile, so it is set from the same
   numbers as the "Zapytania" step (.mkt-wq-plus: the checkbox of its own grid plus 2px, at 5px of
   radius), in px and not in em: tied to this grid's clamped font size the chip grew to 19px on the
   768 frame while the one next door stayed at 18, which is most of what made this tile read as the
   heavier of the two. The px stack lands within a pixel of the .mkt-wc-check beside it at every
   preview width, so the chip still belongs to this grid. */
.mkt-wc-plus {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 17px;
  height: 17px;
  border-radius: 5px;
  background: #5B5CE6;
  color: #FFFFFF;
  font-size: 13px;
  line-height: 1;
}
/* "Dodaj własnego konkurenta" / "Add custom competitor" — the app's own label for this tile, and
   the longest string on the frame. It is never shortened and never ellipsised: a brand name cut
   to "New Balan…" still reads as a name, "Dodaj własne…" on a button tells the reader nothing.
   Where it does not fit a cell on one line it takes a second, which is what the "Zapytania" tile
   does with its own label on the frames where that one is too long.
   Measured, both languages, 380–1100 in 1px steps: the Polish label takes its second line at a
   container of ~450px and below — viewports 380–490 and 960–995 — and the English one at ~405 and
   below, viewports 380–445. Above those it is one line, which is every frame from the 1024
   preview up. Two lines are the most it ever takes: a third would need a cell narrower than a
   two-column grid can make one at any width this page renders, and the tile is held to its
   row-mates' height there by the block inset in the narrow band below.
   Its size, weight and colour are set with the "Zapytania" tile's, in the shared rule at the head
   of this block, and what lets it wrap is set with the face's, up beside the face. */

/* The same threshold the "Zapytania" tile switches on, so the two tiles never step up a size on
   different frames. */
@container (min-width: 601px) {
  .mkt-wc-plus { width: 18px; height: 18px; }
}

/* --- the wide preview --------------------------------------------------------------
 * At 768 the page drops to one column and hands the widget a ~702 px box: app-width, where
 * add-project-step-competitors.tsx itself lays the tiles out in two columns (its md breakpoint
 * starts at 900). The columns are the base set now — this rung only widens the seam, and it does
 * it on 601, the "Zapytania" grid's own threshold, so the two steps never step up on different
 * frames. */

@container (min-width: 601px) {
  .mkt-wc-grid { gap: 8px; }
}

/* --- narrow previews -------------------------------------------------------------
 * The preview is rendered at zoom .9 on wide viewports, so these thresholds are the container's
 * own CSS pixels: ~538 on the desktop preview, ~456 at 1024, ~702 at 768 and ~348 on the phone. */

@container (max-width: 500px) {
  .mkt-widget-body.mkt-wc { gap: 7px; }
  .mkt-wc-status { gap: 7px; }
  .mkt-wc-steps { font-size: 11px; }
  .mkt-wc-search { padding: 3px 3px 3px 8px; }
  .mkt-wc-search-label { font-size: 11px; }
  .mkt-wc-act { width: 20px; height: 20px; border-radius: 6px; }
  .mkt-wc-act svg { width: 12px; height: 12px; }
  .mkt-wc-grid { gap: 6px; }
  .mkt-wc-selected { font-size: 10.5px; }
  .mkt-wc-plus { width: 14px; height: 14px; font-size: 11px; }
  /* Every narrower preview drops the tail of the list instead of squeezing the rows, the way the
     "Zapytania" step does at this same threshold: SIX suggestions, the competitor typed in and
     the fresh tile. The FOUR dropped are the last four (nth-child 7-10) and none of them is
     ticked, so the ticks on screen still add up to the six on the status line.
     Six and not seven, and four dropped and not three: the count is what makes the rows come out
     even. Six half-width cells, the typed-in competitor across the row and the tile across the
     row are ten column-widths — five whole rows. A seventh suggestion would be an eleventh, half
     a row, and half a row is the hole this whole band exists to avoid.
     And the tile takes the whole of its row here. A half-width cell in this band is 144-176px
     against the 165 "Dodaj własnego konkurenta" needs on one line, so the label would take a
     second line — and a second line does not fit the row. This band is the one place where the
     page hands the widget a SHORT square as well as a narrow one: at a viewport of 960-1010 the
     two-column grid has just split and the preview is ~418px wide by 293 tall, which leaves the
     rows 23px each, against the 30px two centred lines of 11px need with nothing but their own
     border around them. The tile stood 6px proud of "Under Armour" beside it. Container queries
     cannot tell that frame from the roomy 470px one — both are ~420-450px of container — so the
     whole band gets the row that fits both: full width, one line, the same height as every cell
     above it — see the block below for what holds it to that height.
     The cell the tile is a face over takes the row with it, for the same reason and to the
     pixel: the face carries the same label and, left half-width, it wrapped to two lines and its
     second line hung 2.5px below the tile's own dashes at 960-995. Six suggestions on three
     rows, your own competitor on the fourth and the tile that adds it on the fifth — five even
     rows, and the press still lands on the cell it turns into.
     The block inset stays tight for the phone rung below, where both are single cells again and
     the label takes the two lines the "Zapytania" tile takes there too. */
  .mkt-wc-grid > .mkt-wc-card:nth-child(n+7):nth-child(-n+10) { display: none; }
  .mkt-wc-card.is-custom,
  .mkt-wc-add { grid-column: span 2; }
  /* ...and the tile is the same height as the cells above it, by the cells' own arithmetic rather
     than by a padding chosen to look about right. This is the one band where the grid's row is
     SHORTER than a cell — the frame is short as well as narrow, the rows come out ~23px and a
     cell needs ~25 — so `min-height: min(100%, 3.4em)` stops governing and each cell stands at
     its own content height instead. A tile with a block padding of its own then stood 2.05px
     short of its row-mates at 960 and closed the gap only by 985.
     So the tile is given no block padding at all and the cells' own height as its floor: the
     grid's row while the row is the taller (every other width, where min(100%, 3.4em) governs)
     and the cell's box while the cell is (this band). The "Zapytania" grid is the same mechanism
     in its simplest form — its cards carry no block padding either, so every cell there is
     exactly its row — and the tile now lands on the same number as its neighbours at every width
     in 380-1100, in both languages, instead of within 2px of it. */
  .mkt-wc-add {
    padding-block: 0;
    min-height: max(min(100%, 3.4em), var(--wc-cell-h));
  }
}

/* A ~348px phone preview: two columns, and the four suggestions that would push the typed-in
   competitor and the add tile out of the frame step aside. The ticks left on screen still add up
   to the count on the status line.
   The narration stays: the dot and the line are the only thing on the frame that says the step is
   working, and dropping them left half a line blank for the whole sequence. At 10.5px the line
   still has ~160px, which carries the last two texts whole and the first two far enough to be
   read — the same trade the GEO Site Audit widget makes in the same box. */
@container (max-width: 360px) {
  /* Both take a single cell again: spanning would push the row they share onto a fifth, and
     there is no need — six suggestions, your own competitor and the tile make eight cells and
     four even rows, and the tile has the room for its two lines here, the phone frame's rows
     being 36px against the 30 two lines of 10.5px need. It is where the "Zapytania" tile takes
     its second line too. */
  .mkt-wc-card.is-custom,
  .mkt-wc-add { grid-column: auto; }
  .mkt-wc-ghost { right: 30%; }
  .mkt-wc-steps { font-size: 10.5px; }
  .mkt-wc-selected { font-size: 10px; margin-left: auto; }
  .mkt-wc-plus { width: 15px; height: 15px; }
}

/* --- sequence --------------------------------------------------------------------- */

/* The frame is furnished from the very first frame: the stepper, the search bar, the eleven
   cells (ten suggestions and the one typed in by hand) and the add tile are all in place at t=0 — nothing fades the form itself in — and only
   the contents of the cells resolve as the lookup returns them. */
.mkt-wc.is-armed .mkt-wc-ghost { animation: wc-ghost 0.36s ease calc(0.12s + var(--i) * 0.075s) both; }
.mkt-wc.is-armed .mkt-wc-name { animation: wc-name-in 0.32s cubic-bezier(0.2, 0.9, 0.3, 1.2) calc(0.3s + var(--i) * 0.075s) both; }

/* The five most cited are picked in turn. */
.mkt-wc.is-armed .mkt-wc-card.is-on:not(.is-custom) { animation: wc-card-pick 0.34s ease calc(1.3s + var(--k) * 0.11s) backwards; }
.mkt-wc.is-armed .mkt-wc-card.is-on:not(.is-custom) .mkt-wc-check { animation: wc-check-pick 0.34s cubic-bezier(0.2, 0.9, 0.3, 1.3) calc(1.3s + var(--k) * 0.11s) backwards; }
.mkt-wc.is-armed .mkt-wc-card.is-on:not(.is-custom) .mkt-wc-check svg { animation: wc-fade-in 0.18s ease calc(1.4s + var(--k) * 0.11s) backwards; }

/* The search bar keeps pace: the refresh button spins through the lookup, and select-all turns
   indeterminate once part of the list is picked. */
/* Two turns is more than the 1.15 s this button spins for: counted rather than infinite, so
   nothing is left rotating (invisibly) on the page once the step has settled. */
.mkt-wc.is-armed .mkt-wc-act-spin { animation: wc-spin 0.7s linear 2, wc-window-hard 1.15s linear both; }
.mkt-wc.is-armed .mkt-wc-act.is-refresh .mkt-wc-act-ico { animation: wc-fade-in 0.25s ease 1.15s both; }
.mkt-wc.is-armed .mkt-wc-act.is-all .is-empty { animation: wc-fade-out 0.2s ease 1.42s both; }
.mkt-wc.is-armed .mkt-wc-act.is-all .is-some { animation: wc-fade-in 0.2s ease 1.42s both; }

/* The tile is pressed and the cell UNDER it becomes your row: the face is an overlay on the cell
   the app appends, so the press and what the press produces are one place on the frame, the way
   the "Zapytania" step has always done it. The cell was a loading row with the tile standing
   somewhere else, which put the cause a column away from the effect and made the two onboarding
   steps read as two different controls.
   2.15 press, 2.30 the face is off, 2.32 the row is ticked — addCustomCompetitorRow() selects the
   new index as it creates it — and 2.40 the placeholder is under a caret. The offsets are the
   "Zapytania" step's (2.14 / 2.30), so the same gesture takes the same time in both. */
.mkt-wc.is-armed .mkt-wc-face { animation: wc-face-out 0.3s ease 2.3s backwards; }
.mkt-wc.is-armed .mkt-wc-face .mkt-wc-plus { animation: wc-press 0.3s ease 2.15s; }
.mkt-wc.is-armed .mkt-wc-card.is-custom { animation: wc-card-pick 0.34s ease 2.32s backwards; }
.mkt-wc.is-armed .mkt-wc-card.is-custom .mkt-wc-check { animation: wc-check-pick 0.34s cubic-bezier(0.2, 0.9, 0.3, 1.3) 2.32s backwards; }
.mkt-wc.is-armed .mkt-wc-card.is-custom .mkt-wc-check svg { animation: wc-fade-in 0.18s ease 2.42s backwards; }
/* The focus ring the new input carries until the name is committed. */
.mkt-wc.is-armed .mkt-wc-card.is-custom::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  /* Inwards: the card clips its own overflow, so an outward spread is cut off at the border and
     never reads as a ring. A 2px rim over a soft halo is what the app's focused input shows. */
  box-shadow: inset 0 0 0 2px rgba(91, 92, 230, 0.55), inset 0 0 0 6px rgba(91, 92, 230, 0.13);
  opacity: 0;
  pointer-events: none;
  animation: wc-ring 1.01s ease-out 2.35s both;
}
/* The fresh tile the wizard appends behind your row. It holds a loading row of its own from the
   first frame so the grid never has a hole in it, and its chrome resolves out of the same grey
   the other loading rows use once the competitor above it has been counted (3.36) — then the plus
   and its label pop in. Same three beats, same order and the same 0.08s between them as the
   "Zapytania" step's fresh tile, counted off its own commit instead of that one's. */
.mkt-wc.is-armed .mkt-wc-add .mkt-wc-skel { animation: wc-skel-out 0.3s ease 3.34s both; }
.mkt-wc.is-armed .mkt-wc-add { animation: wc-add-arrive 0.4s ease 3.34s backwards; }
.mkt-wc.is-armed .mkt-wc-add > .mkt-wc-plus,
.mkt-wc.is-armed .mkt-wc-add > .mkt-wc-add-text { animation: wc-pop 0.36s cubic-bezier(0.2, 0.9, 0.3, 1.2) 3.42s backwards; }
.mkt-wc.is-armed .mkt-wc-ph { animation: wc-window-hard 0.2s linear 2.4s both; }
.mkt-wc.is-armed .mkt-wc-field { animation: wc-type 0.7s steps(var(--n)) 2.55s both, wc-fade-in 0.18s ease 2.32s backwards; }
.mkt-wc.is-armed .mkt-wc-caret { animation: wc-caret 0.96s steps(1, end) 2.4s both; }

/* Five turns covers the 3.36 s up to the tick, then stops with the widget. */
.mkt-wc.is-armed .mkt-wc-spinner { animation: wc-spin 0.7s linear 5, wc-hold 3.36s linear both; }
/* The tick and the line that says the list is ready land together at 3.36 s, hold for a beat and
   clear together: what stays on the settled frame is the list and its count, not the alert. */
.mkt-wc.is-armed .mkt-wc-tick { animation: wc-settle 1.75s ease 3.36s both; }
/* The four status lines share one grid cell, so only one of them may ever carry ink. They run on
   a single 3.6 s track, and each line's window ends at exactly the percentage where the next
   one's begins (35% = 1.26 s, 58.3% = 2.10 s, 93.3% = 3.36 s), so two texts can never overprint.
   The outgoing line leaves in 70 ms, the incoming one arrives in 130 ms with a small rise: a swap
   that is read as movement, not as a blink, and that survives being paused on any frame. */
.mkt-wc.is-armed .mkt-wc-steps > .is-lookup { animation: wc-line-lookup 3.6s linear both; }
.mkt-wc.is-armed .mkt-wc-steps > .is-pick { animation: wc-line-pick 3.6s linear both; }
.mkt-wc.is-armed .mkt-wc-steps > .is-custom { animation: wc-line-custom 3.6s linear both; }
.mkt-wc.is-armed .mkt-wc-steps > .is-done { animation: wc-line-done 1.75s ease 3.36s both; }
.mkt-wc.is-armed .mkt-wc-bar i { animation: wc-bar 3.5s linear both; }

.mkt-wc.is-armed .mkt-wc-num.is-sel { animation: wc-count-selected 3.6s step-end both; }

/* Waiting for marketing.js to mark the widget live: hold the first frame. */
.mkt-widget:not(.is-live) .mkt-wc.is-armed *,
.mkt-widget:not(.is-live) .mkt-wc.is-armed *::after,
.mkt-widget:not(.is-live) .mkt-wc.is-armed *::before { animation-play-state: paused !important; }

@media (prefers-reduced-motion: reduce) {
  .mkt-wc *,
  .mkt-wc *::after,
  .mkt-wc *::before { animation: none !important; }
}

@keyframes wc-name-in { from { opacity: 0; translate: 0 3px; } }
@keyframes wc-ghost { 0% { opacity: 1; } 55% { opacity: 1; } 100% { opacity: 0; } }
@keyframes wc-skel-out { from { opacity: 1; } to { opacity: 0; } }
/* The tile holds the look of a loading row until its moment; border-style is a discrete
   property, so the dashes snap in halfway through, under the loading row still fading out. */
@keyframes wc-add-arrive { from { border-color: #EDEEF2; border-style: solid; background: #FFFFFF; } }
@keyframes wc-face-out { from { opacity: 1; } }
@keyframes wc-pop { from { opacity: 0; scale: 0.94; } }
@keyframes wc-card-pick { from { border-color: #EDEEF2; background: #FFFFFF; box-shadow: inset 0 0 0 1px rgba(91, 92, 230, 0); } }
@keyframes wc-check-pick { from { background: #FFFFFF; border-color: #C4C8D6; scale: 1; } 60% { scale: 1.25; } }
@keyframes wc-press { 45% { scale: 0.88; } }
@keyframes wc-ring { 0% { opacity: 0; } 8%, 72% { opacity: 1; } 100% { opacity: 0; } }
@keyframes wc-type { from { --wc-t: 0; } to { --wc-t: 1; } }
@keyframes wc-caret {
  0%, 16.6%, 33.3%, 50%, 66.6%, 83.3% { opacity: 1; }
  8.3%, 25%, 41.6%, 58.3%, 75%, 91.6%, 100% { opacity: 0; }
}
@keyframes wc-spin { to { rotate: 360deg; } }
@keyframes wc-hold { 0%, 96% { opacity: 1; } 100% { opacity: 0; } }
@keyframes wc-fade-in { from { opacity: 0; } }
@keyframes wc-fade-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes wc-window-hard { 0%, 99% { opacity: 1; } 100% { opacity: 0; } }
/* The status line, one 3.6 s track per line, 1% = 36 ms. The windows meet — never overlap — at
   35% (1.26 s), 58.3% (2.10 s) and 93.3% (3.36 s). */
@keyframes wc-line-lookup { 0%, 33.1% { opacity: 1; translate: 0 0; } 35%, 100% { opacity: 0; translate: 0 -4px; } }
@keyframes wc-line-pick { 0%, 35% { opacity: 0; translate: 0 4px; } 38.6%, 56.4% { opacity: 1; translate: 0 0; } 58.3%, 100% { opacity: 0; translate: 0 -4px; } }
@keyframes wc-line-custom { 0%, 58.3% { opacity: 0; translate: 0 4px; } 61.9%, 91.4% { opacity: 1; translate: 0 0; } 93.3%, 100% { opacity: 0; translate: 0 -4px; } }
/* The last line and the tick have a 1.75 s track of their own, started at the instant the line
   before them has cleared (3.36 s): in over 130 ms, held for 1.1 s, gone by 5.11 s. */
@keyframes wc-line-done { 0% { opacity: 0; translate: 0 4px; } 7.4%, 70.3% { opacity: 1; translate: 0 0; } 100% { opacity: 0; translate: 0 -3px; } }
@keyframes wc-settle { 0% { opacity: 0; scale: 0.8; } 9% { opacity: 1; scale: 1.08; } 16%, 70.3% { opacity: 1; scale: 1; } 100% { opacity: 0; scale: 1; } }
@keyframes wc-bar { 0% { transform: scaleX(0); } 30% { transform: scaleX(0.32); } 55% { transform: scaleX(0.55); } 68% { transform: scaleX(0.64); } 96%, 100% { transform: scaleX(1); } }
/* Each step lands 55 ms inside the tick it is counting, where the box it counts is already past
   half filled (the pick easing is front-loaded): the number never trails a tick that reads as on,
   and never runs ahead of one that has not begun. Picks start at 1.30 + k · 0.11 s; the
   hand-typed row is appended already selected at 2.32 s. Percentages of the 3.6 s track. */
@keyframes wc-count-selected {
  0% { --wc-n: 0; }
  37.6% { --wc-n: 1; }
  40.4% { --wc-n: 2; }
  43.8% { --wc-n: 3; }
  46.5% { --wc-n: 4; }
  49.9% { --wc-n: 5; }
  65.6% { --wc-n: 6; }
}

/* === wizard-queries ======================================================== */
/*
 * Product page preview — /produkt-claude-preview, step 1 "Onboarding" › "Prompty dobrane przez AI"
 * (widgets/v2/wizard-queries.blade.php).
 *
 * Every rule below describes the finished step: the suggested prompts (twelve are rendered, and
 * the narrower previews drop the tail rather than squeeze the rows), five pre-selected, one more
 * ticked by hand, one typed in, seven queries to track. The sequence that leads there is a set
 * of keyframe animations whose "from" frames hold the earlier states. They apply only once the
 * widget is armed by its inline script, sit paused until marketing.js marks the widget live, and
 * restart by themselves each time the accordion shows the widget again (display: none cancels CSS
 * animations).
 *
 * Timeline (s)
 *   0.00        not one empty cell: a loading row in every card and, on the card you will type
 *               into, the dashed "+" face; the spinner turns
 *   0.16–1.09   the prompts land one by one, each loading row dissolving into its text
 *   1.32–1.72   the five the lookup pre-selects tick themselves
 *   2.20        you tick a sixth by hand (a ring marks the deliberate click)
 *   2.30–3.52   the "+" face is pressed, the card becomes an input, and your own query is typed
 *   3.50–3.94   it commits, the last loading row becomes a fresh "+" tile, the counter lands on 7
 *   3.50–5.25   the step says it is done, holds for a beat and clears itself: what stays on the
 *               settled frame is the list and its counter, never the "done" line — which is why
 *               that line is invisible in the base styles the no-JS and reduced-motion frames use
 */

@property --wq-sel-n { syntax: '<integer>'; inherits: false; initial-value: 0; }
@property --wq-t { syntax: '<number>'; inherits: true; initial-value: 0; }

.mkt-widget-body.mkt-wq {
  gap: 8px;
  container-type: inline-size;
}

.mkt-wq-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- status line ------------------------------------------------------------- */

.mkt-wq-status { display: flex; align-items: center; gap: 9px; min-height: 18px; }
.mkt-wq-state { position: relative; width: 14px; height: 14px; flex: 0 0 auto; }
.mkt-wq-spinner {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid #E3E4FA;
  border-top-color: #5B5CE6;
  opacity: 0;
}
/* Hidden until the list lands, and hidden again once it has: the tick belongs to that moment. */
.mkt-wq-tick { position: absolute; inset: 0; border-radius: 50%; background: #22C55E; opacity: 0; }
.mkt-wq-tick::after {
  content: "";
  position: absolute;
  left: 4.7px;
  top: 3px;
  width: 3.4px;
  height: 6.2px;
  border: solid #FFFFFF;
  border-width: 0 1.6px 1.6px 0;
  transform: rotate(45deg);
}
/* 12px, not 12.5: the self-hosted Poppins 500 face opens visible gaps inside words at 12.5px in
   Chromium ("List a zapytań got owa"), and it is the glyph advances that move, so letter-spacing
   cannot close them. 12px renders the same string cleanly — the "Konkurenci" step sets it too. */
.mkt-wq-steps { display: grid; flex: 1; min-width: 0; font-size: 12px; font-weight: 500; color: #262A47; }
/* Every line, the last one included, starts and ends invisible: the narration belongs to the
   sequence, not to the frame it leaves behind. */
.mkt-wq-steps > span { grid-area: 1 / 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; opacity: 0; }

/* The end of the same line: the app's limit counter (add-project-step-queries-limit-counter),
   ticking up with the boxes below it and staying there after the narration has cleared. It is the
   one number the step is about, and it sits where the "Konkurenci" step keeps its own. */
.mkt-wq-selected { flex: 0 0 auto; font-size: 11.5px; color: #4C5163; white-space: nowrap; }
/* inline-block so the number can pop when it lands: `scale` does not apply to inline boxes.
   --to is the settled count and comes from the blade, off the same $selected the screen-reader
   span carries, so the three can never drift apart; the literal is a last-resort fallback only. */
.mkt-wq-sel-num { display: inline-block; --wq-sel-n: var(--to, 7); counter-reset: wqsel var(--wq-sel-n); font-weight: 650; color: #14162B; }
.mkt-wq-sel-num::after { content: counter(wqsel); }

.mkt-wq-bar { height: 3px; margin-top: -3px; border-radius: 3px; background: #EDEEF2; overflow: hidden; }
.mkt-wq-bar i { display: block; height: 100%; background: #5B5CE6; transform-origin: 0 50%; }

/* --- the prompt grid ----------------------------------------------------------
 * The suggestions, the query typed in by hand and the fresh "+" tile after it, on equal rows that
 * share the frame: fewer, taller cells with air between them read as a list you could work
 * through, where a full wall of them reads as a wall of text. Each preview width keeps as many
 * rows as it can give ~35px to (see the container queries at the end of this block). The paddings
 * live in custom properties because the loading row has to sit exactly on top of the card it
 * stands in for, at every size. */

.mkt-wq-grid {
  flex: 1 1 0;
  min-height: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-auto-rows: 1fr;
  gap: 7px;
}

.mkt-wq-card {
  --wq-pad: 9px;
  --wq-check: 15px;
  --wq-gap: 7px;
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--wq-gap);
  min-width: 0;
  padding: 0 var(--wq-pad);
  border: 1px solid #EDEEF2;
  border-radius: 8px;
  background: #FFFFFF;
  overflow: hidden;
}
.mkt-wq-card.is-on {
  border-color: #5B5CE6;
  background: #F7F8FE;
  box-shadow: inset 0 0 0 1px #5B5CE6;
}

.mkt-wq-check {
  width: var(--wq-check);
  height: var(--wq-check);
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1.5px solid #C4C8D6;
  border-radius: 4.5px;
  background: #FFFFFF;
  color: transparent;
}
.mkt-wq-card.is-on .mkt-wq-check { border-color: #5B5CE6; background: #5B5CE6; color: #FFFFFF; }
.mkt-wq-check svg { width: 0.64em; height: 0.64em; }

/* --wz-text is the wizard's shared scale, set at the head of the "Konkurenci" block for both
   grids; the prompts are what it is cut to. Its 11px floor is not 9: between ~500 and ~560px of
   container the slope alone fell to 10.7px, and the wide previews are drawn at zoom .9, so that is
   9.6px of rendered size — below it the self-hosted Poppins face drops the spaces between words
   ("Najlepsze buty dobiegania 2026"). 11px is the smallest size that renders every prompt cleanly
   there; the phone preview, which carries no zoom, is the only place a smaller one is set.
 *
 * Two lines, and never a cut. A prompt the cell cannot hold on one line takes a second one
   instead of ending in an ellipsis — at every preview size, in both languages: the prompts are
   what the step is about, and a query the reader cannot finish reading is not a suggestion.
   No line clamp here: the box is a flex item, so a `-webkit-box` display would be blockified away
   and the declaration would do nothing. The row height is what holds the text to two lines —
   grid-auto-rows: 1fr fixes it off the frame, and the card clips what will not fit — and the
   longest prompt in either language wants 1.9 of them at the tightest preview. */
.mkt-wq-text {
  min-width: 0;
  font-size: var(--wz-text);
  line-height: 1.22;
  color: #14162B;
  overflow: hidden;
}

/* The loading row: an opaque copy of the card's own geometry, so the grey box under the skeleton
   bar is exactly where the checkbox will be and the bar is exactly where the prompt will be. */
.mkt-wq-skel {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: #FFFFFF;
  opacity: 0;
  pointer-events: none;
}
.mkt-wq-skel::before {
  content: "";
  position: absolute;
  left: var(--wq-pad);
  top: 50%;
  translate: 0 -50%;
  width: var(--wq-check);
  height: var(--wq-check);
  border-radius: 4.5px;
  background: #EDEFF4;
}
.mkt-wq-skel::after {
  content: "";
  position: absolute;
  left: calc(var(--wq-pad) + var(--wq-check) + var(--wq-gap));
  top: 50%;
  translate: 0 -50%;
  width: calc(var(--w) * 1%);
  max-width: calc(100% - var(--wq-pad) * 2 - var(--wq-check) - var(--wq-gap));
  height: 6px;
  border-radius: 3px;
  background-color: #EDEFF4;
  background-image: linear-gradient(90deg, #EDEFF4 0%, #F8F9FC 45%, #EDEFF4 82%);
  background-size: 260% 100%;
}

/* The "+" tile: the same card with the dashed, lilac add face — and 9px of radius, not the 8/10px
   the cards under it take: the add tile is the same tile in both onboarding steps and is drawn to
   one radius in both, the one .mkt-wq-face already uses over these same cards. */
.mkt-wq-card.is-add {
  border: 1.5px dashed #5B5CE6;
  border-radius: 9px;
  background: #EEF0FE;
}
/* Its label is set with the "Konkurenci" tile's, in the shared rule at the head of that block. */
.mkt-wq-plus {
  width: calc(var(--wq-check) + 2px);
  height: calc(var(--wq-check) + 2px);
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 5px;
  background: #5B5CE6;
  color: #FFFFFF;
  font-size: 13px;
  line-height: 1;
}

/* --- the query typed in by hand ----------------------------------------------- */

.mkt-wq-type {
  position: relative;
  flex: 0 1 auto;
  width: max-content;
  max-width: 100%;
  min-width: 0;
  --wq-t: 1;
}
/* The padding is load-bearing, not decoration. The box is width: max-content, so without it the
   last glyph's ink ends exactly on the edge that both overflow: hidden and the clip-path cut at —
   and on the zoomed previews (.9 above a 1000px viewport) that edge lands mid-pixel and shaves the
   final "?" flat. Two pixels of slack put the boundary clear of the ink at every width. */
.mkt-wq-type-text {
  display: block;
  padding-right: 2px;
  font-size: var(--wz-text);
  line-height: 1.22;
  color: #14162B;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  clip-path: inset(-20% calc((1 - var(--wq-t)) * 100%) -20% 0);
}
.mkt-wq-caret {
  position: absolute;
  top: 50%;
  left: calc(var(--wq-t) * 100%);
  translate: 0 -50%;
  width: 1.4px;
  height: 1.1em;
  border-radius: 1px;
  background: #5B5CE6;
  opacity: 0;
}

/* The add face that covers the card until it is clicked. */
.mkt-wq-face {
  position: absolute;
  inset: -1px;
  display: flex;
  align-items: center;
  gap: var(--wq-gap);
  padding: 0 var(--wq-pad);
  border: 1.5px dashed #5B5CE6;
  border-radius: 9px;
  background: #EEF0FE;
  opacity: 0;
}
.mkt-wq-face-text {
  min-width: 0;
  overflow: hidden;
}

/* --- preview sizes --------------------------------------------------------------
 * The preview is rendered at zoom .9 on wide viewports, so these thresholds are the container's
 * own CSS pixels: ~662 on the single-column tablet preview, ~558 on the desktop one, ~467 at
 * 1024, ~378 where the page first hands the widget its narrow column (~960) and ~308 on the
 * phone. */

/* The roomy single-column preview: the whole rendered list, on cells with room around the text.
   The type steps up with --wz-text, on the same threshold the "Konkurenci" grid uses. */
@container (min-width: 601px) {
  .mkt-wq-grid { gap: 8px; }
  .mkt-wq-card { --wq-pad: 12px; --wq-check: 16px; --wq-gap: 9px; border-radius: 10px; }
  .mkt-wq-skel::after { height: 7px; }
}

/* Every narrower preview drops the tail of the list instead of squeezing the rows. From here down
   the frame shows the first eight suggestions — the five pre-selected, two left out and the one
   ticked by hand — plus the query typed in and the fresh "+" tile: ten cells on five rows. The six
   ticked suggestions are the first eight cards, so the ticks on screen still add up to the counter
   on the status line, and the two rows given back are what buys every remaining row its air. The
   1024 and ~960 frames are the shortest on the page and set that floor. */
@container (max-width: 500px) {
  .mkt-widget-body.mkt-wq { gap: 7px; }
  .mkt-wq-status { gap: 7px; }
  .mkt-wq-steps { font-size: 11px; }
  .mkt-wq-selected { font-size: 10.5px; }
  .mkt-wq-grid { gap: 6px; }
  .mkt-wq-grid > .mkt-wq-card:nth-child(n+9):nth-child(-n+12) { display: none; }
  /* Three pixels of chrome given back to the text. */
  .mkt-wq-card { --wq-pad: 7px; --wq-check: 12px; --wq-gap: 5px; border-radius: 8px; }
  .mkt-wq-skel::after { height: 6px; }
  .mkt-wq-plus { font-size: 11px; }
  /* A cell of this band is 150–200px wide, and the query you type in ("Nike Pegasus 41 czy
     Structure 25?") wants 195 of them — so here it wraps like the suggestions do. A left-to-right
     reveal would then uncover both lines at once, so it fades in instead, slowly enough to still
     read as being filled in; the caret belongs to the reveal and goes with it. Container queries
     carry no specificity of their own, so the animation rule has to out-specify the one further
     down the file to win. */
  .mkt-wq-type { flex: 1 1 auto; width: auto; }
  .mkt-wq-type-text {
    white-space: normal;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
  }
  .mkt-wq-caret { display: none; }
  /* It starts while the "+" face is still fading off the card, so the two dissolve into each
     other and the cell is never left standing empty. */
  .mkt-wq.is-armed .mkt-wq-card.is-own .mkt-wq-type { animation: wq-fade-in 0.7s ease 2.5s both; }
}

/* The phone preview is ~310px of content: the same ten cells as the band above, on half the line
   of text, so the prompts take their two lines at the size the Poppins face still renders with its
   word spaces intact.
   The narration stays, at the same 10.5px the "Konkurenci" step next door sets: the spinner and
   the line are the only thing on the frame that says the step is working, and the GEO Site Audit
   widget makes the same choice in the same box. What gives here is the copy, not the line — all
   four texts are written to fit the ~151px this frame leaves beside the count, in both languages,
   so none of them ends in an ellipsis. Beside them the count keeps the line from standing empty
   once the narration has cleared. */
@container (max-width: 360px) {
  .mkt-wq-status { gap: 6px; }
  .mkt-wq-steps { font-size: 10.5px; }
  .mkt-wq-selected { font-size: 10px; }
  .mkt-wq-grid { gap: 6px; }
  .mkt-wq-card { --wq-check: 13px; }
}

/* --- sequence ------------------------------------------------------------------- */

/* No entrance for the grid itself: every card is on screen in the very first frame, loading, and
   it is their contents that stream in. */
.mkt-wq.is-armed .mkt-wq-skel { animation: wq-skel-out 0.26s ease calc(0.16s + var(--i) * 0.052s) both; }
.mkt-wq.is-armed .mkt-wq-skel::after { animation: wq-shimmer 1.15s linear infinite; }
.mkt-wq.is-armed .mkt-wq-card[style*="--i"] .mkt-wq-text { animation: wq-text-in 0.32s ease calc(0.2s + var(--i) * 0.052s) both; }

/* The pre-selected cards are ticked where they stand; the tick has to hold the unselected look
   until its moment, so the "from" frame carries it. */
.mkt-wq.is-armed .mkt-wq-card.is-on:not(.is-own) { animation: wq-card-pick 0.32s ease var(--t) backwards; }
.mkt-wq.is-armed .mkt-wq-card.is-on:not(.is-own) .mkt-wq-check { animation: wq-check-pick 0.34s cubic-bezier(0.2, 0.9, 0.3, 1.35) var(--t) backwards; }
.mkt-wq.is-armed .mkt-wq-card.is-on:not(.is-own) .mkt-wq-check svg { animation: wq-fade-in 0.18s ease calc(var(--t) + 0.1s) backwards; }
/* The one you tick yourself gets a ring, so the hand-made pick reads as deliberate. */
.mkt-wq.is-armed .mkt-wq-card.is-user::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 0 0 2px rgba(91, 92, 230, 0.55);
  opacity: 0;
  pointer-events: none;
  animation: wq-ring 0.8s ease-out 2.18s;
}

/* The "+" tile is pressed, the card underneath becomes an input, and the query is typed in. */
.mkt-wq.is-armed .mkt-wq-face { animation: wq-face-out 0.3s ease 2.3s backwards; }
.mkt-wq.is-armed .mkt-wq-face .mkt-wq-plus { animation: wq-press 0.3s ease 2.14s; }
.mkt-wq.is-armed .mkt-wq-type { animation: wq-type 0.9s steps(var(--n)) 2.62s both; }
/* The caret stops with the last keystroke; the query then commits, and only once it is counted
   does the fresh "+" tile arrive and the step report itself done. */
.mkt-wq.is-armed .mkt-wq-caret { animation: wq-caret 1.12s steps(1, end) 2.4s forwards; }
.mkt-wq.is-armed .mkt-wq-card.is-own { animation: wq-own-commit 0.3s ease 3.52s backwards; }
.mkt-wq.is-armed .mkt-wq-card.is-own .mkt-wq-check { animation: wq-check-pick 0.34s cubic-bezier(0.2, 0.9, 0.3, 1.35) 3.52s backwards; }
.mkt-wq.is-armed .mkt-wq-card.is-own .mkt-wq-check svg { animation: wq-fade-in 0.18s ease 3.62s backwards; }
/* The fresh "+" tile holds a loading row of its own from the very first frame, so the grid never
   has a hole in it. The row dissolves, the tile's chrome resolves under it out of the same grey the
   other loading rows use, and only then do the plus and its label pop in. */
.mkt-wq.is-armed .mkt-wq-card.is-add .mkt-wq-skel { animation: wq-skel-out 0.3s ease 3.5s both; }
.mkt-wq.is-armed .mkt-wq-card.is-add { animation: wq-add-arrive 0.4s ease 3.5s backwards; }
.mkt-wq.is-armed .mkt-wq-card.is-add > .mkt-wq-plus,
.mkt-wq.is-armed .mkt-wq-card.is-add > .mkt-wq-text { animation: wq-pop 0.36s cubic-bezier(0.2, 0.9, 0.3, 1.2) 3.58s backwards; }

.mkt-wq.is-armed .mkt-wq-spinner { animation: wq-spin 0.7s linear infinite, wq-hold 3.5s linear both; }
/* The tick and the line it belongs to arrive together, hold for a beat and clear together: a step
   that is finished is told by the list and its counter, not by a notice left on the frame. */
.mkt-wq.is-armed .mkt-wq-tick { animation: wq-settle 1.75s ease 3.5s both; }
/* The four status lines share one grid cell, so every handover has to avoid both printing one line
   over the other and letting the row go empty. wq-window fades its line out across its last 14%,
   and each next line starts inside that tail, so the swaps read as cross-fades and not as a cut. */
.mkt-wq.is-armed .mkt-wq-steps > .is-gen { animation: wq-fade-out 0.18s ease 1.14s both; }
.mkt-wq.is-armed .mkt-wq-steps > .is-pick { animation: wq-window 1.06s ease 1.28s both; }
/* The last handover is the one that has to hold up: "Dodajesz własne zapytanie" runs until the
   typed query commits at 3.52s, so "Gotowa" starts to rise just before that. It is already a third
   of the way up while the outgoing line still has a third left, which keeps the row from ever
   dipping towards empty the way a symmetrical hand-over does. Then it holds for 1.2s — long enough
   to read at a glance — and leaves, and the row is the counter alone. */
.mkt-wq.is-armed .mkt-wq-steps > .is-add { animation: wq-window 1.3s ease 2.3s both; }
.mkt-wq.is-armed .mkt-wq-steps > .is-done { animation: wq-line-done 1.75s ease 3.5s both; }
.mkt-wq.is-armed .mkt-wq-bar i { animation: wq-bar 3.74s linear both; }
.mkt-wq.is-armed .mkt-wq-sel-num { animation: wq-sel-count 4s step-end both, wq-sel-land 0.4s cubic-bezier(0.2, 0.9, 0.3, 1.4) 3.52s; }

/* Waiting for marketing.js to mark the widget live: hold the first frame. */
.mkt-widget:not(.is-live) .mkt-wq.is-armed *,
.mkt-widget:not(.is-live) .mkt-wq.is-armed *::after,
.mkt-widget:not(.is-live) .mkt-wq.is-armed *::before { animation-play-state: paused !important; }

@media (prefers-reduced-motion: reduce) {
  .mkt-wq *,
  .mkt-wq *::after,
  .mkt-wq *::before { animation: none !important; }
}

@keyframes wq-pop { from { opacity: 0; scale: 0.94; } }
/* The "+" tile holds the look of a loading row until its moment; border-style is a discrete
   property, so the dashes snap in halfway through, under the loading row still fading out. */
@keyframes wq-add-arrive { from { border-color: #EDEEF2; border-style: solid; background: #FFFFFF; } }
@keyframes wq-fade-in { from { opacity: 0; } }
@keyframes wq-fade-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes wq-skel-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes wq-shimmer { from { background-position: 130% 0; } to { background-position: -130% 0; } }
@keyframes wq-text-in { from { opacity: 0; translate: 0 3px; } }
@keyframes wq-card-pick { from { border-color: #EDEEF2; background: #FFFFFF; box-shadow: inset 0 0 0 1px rgba(91, 92, 230, 0); } }
@keyframes wq-check-pick {
  from { border-color: #C4C8D6; background: #FFFFFF; scale: 1; }
  60% { scale: 1.22; }
}
@keyframes wq-ring { from { opacity: 1; scale: 1; } to { opacity: 0; scale: 1.05; } }
@keyframes wq-press { 45% { scale: 0.88; } }
@keyframes wq-face-out { from { opacity: 1; } }
@keyframes wq-own-commit { from { border-color: #C7C9F7; background: #FFFFFF; box-shadow: inset 0 0 0 1px rgba(91, 92, 230, 0); } }
@keyframes wq-type { from { --wq-t: 0; } to { --wq-t: 1; } }
@keyframes wq-caret {
  0%, 16.6%, 33.3%, 50%, 66.6%, 83.3% { opacity: 1; }
  8.3%, 25%, 41.6%, 58.3%, 75%, 91.6%, 100% { opacity: 0; }
}
@keyframes wq-spin { to { rotate: 360deg; } }
@keyframes wq-hold { 0%, 96% { opacity: 1; } 100% { opacity: 0; } }
@keyframes wq-window { 0% { opacity: 0; translate: 0 4px; } 14%, 86% { opacity: 1; translate: 0 0; } 100% { opacity: 0; translate: 0 -4px; } }
/* Rise, hold, leave — 1.75s of which 0.13s in, 1.10s legible and 0.52s out. The tick runs the same
   track so the two never part company. */
@keyframes wq-line-done { 0% { opacity: 0; translate: 0 4px; } 7.4%, 70.3% { opacity: 1; translate: 0 0; } 100% { opacity: 0; translate: 0 -3px; } }
@keyframes wq-settle { 0% { opacity: 0; scale: 0.8; } 9% { opacity: 1; scale: 1.08; } 16%, 70.3% { opacity: 1; scale: 1; } 100% { opacity: 0; scale: 1; } }
@keyframes wq-bar { 0% { transform: scaleX(0); } 33% { transform: scaleX(0.35); } 47% { transform: scaleX(0.47); } 62% { transform: scaleX(0.6); } 96%, 100% { transform: scaleX(1); } }
@keyframes wq-sel-land { 40% { scale: 1.28; } }
/* The stops cannot be derived in CSS, so they are the 't' values in $prompts read off a 4s
   timeline: 1.32/1.42/1.52/1.62/1.72s for the pre-selected five, 2.20s for the one ticked by hand
   and 3.52s for the query typed in. Change a 't', or add or drop a prompt, and these must follow —
   as must $selected, which is what the settled number and the "Konkurenci" widget's chip read. */
@keyframes wq-sel-count {
  0% { --wq-sel-n: 0; }
  33% { --wq-sel-n: 1; }
  35.5% { --wq-sel-n: 2; }
  38% { --wq-sel-n: 3; }
  40.5% { --wq-sel-n: 4; }
  43% { --wq-sel-n: 5; }
  55% { --wq-sel-n: 6; }
  88% { --wq-sel-n: 7; }
}
