/* Hand-written marketing keyframes. Kept out of Styles/tailwind.css because the Tailwind
   MSBuild target overwrites wwwroot/css/dist.css on every build; this file is committed. */

.gradient-text {
    background: linear-gradient(135deg, #ffffff 0%, #6ee7b7 50%, #34d399 100%);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

.glow-border {
    box-shadow: 0 0 40px rgba(16, 185, 129, 0.08), 0 0 80px rgba(16, 185, 129, 0.04);
}

.cta-glow { transition: all 0.2s ease; }
.cta-glow:hover {
    box-shadow: 0 0 24px rgba(16, 185, 129, 0.25);
    transform: translateY(-1px);
}

/* Hidden-by-default is opt-in: this rule is gated behind .mn-js (added by
   marketing-anim.js as its first statement), so a page where that script never loads
   or never runs keeps every .fade-up section visible instead of permanently blank. If
   the script does run but its reveal setup throws (e.g. IntersectionObserver
   unavailable), marketing-anim.js's own catch block removes .mn-js and reveals
   everything — the guarantee holds end-to-end, not just for failures before this gate
   is set. Must stay ahead of .fade-up.visible and .mn-reduced .fade-up below in source
   order — both share this rule's specificity (two classes), so with the cascade tied
   on specificity, source order decides, and both need to win over this one. */
.mn-js .fade-up {
    opacity: 0;
    transform: translateY(20px);
    transition:
        opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1) var(--mn-delay, 0ms),
        transform 0.6s cubic-bezier(0.16, 1, 0.3, 1) var(--mn-delay, 0ms);
}

.fade-up.visible {
    opacity: 1;
    transform: translateY(0);
}

/* Reduced motion: land every element on its final frame immediately. The mock UI still
   communicates the product — it just does not move. */
.mn-reduced .fade-up {
    opacity: 1;
    transform: none;
    transition: none;
}

.mn-reduced *,
.mn-reduced *::before,
.mn-reduced *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    animation-delay: 0ms !important;
    transition-duration: 0.001ms !important;
}

@media (prefers-reduced-motion: reduce) {
    .fade-up { opacity: 1; transform: none; transition: none; }
}

.mn-chat__window {
    border: 1px solid rgb(39 39 42 / 0.6);
    background: rgb(24 24 27 / 0.6);
    border-radius: 0.75rem;
    padding: 1rem 1.15rem;
    font-size: 0.875rem;
    line-height: 1.6;
}
.mn-chat__label { font-size: 0.7rem; letter-spacing: 0.15em; text-transform: uppercase; color: #71717a; margin-bottom: 0.5rem; }
.mn-chat__prompt { color: #e4e4e7; padding-bottom: 0.75rem; border-bottom: 1px solid rgb(39 39 42 / 0.6); }
.mn-chat__tool {
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.75rem;
    color: #52525b;
    margin-top: 0.6rem;
    opacity: 0;
    animation: mn-tool-in 0.4s ease forwards;
    /* Delay comes from --mn-i (the row's loop index, set inline by AgentChat.razor), not
       :nth-of-type — nth-of-type counts <div> siblings, not elements sharing this class, so it
       silently mis-numbers rows because .mn-chat__prompt and .mn-chat__answer are also <div>s
       in the same parent. Base + step*index reproduces the original 0.5s / 0.9s stagger for
       two rows and keeps working for any row count. */
    animation-delay: calc(0.5s + var(--mn-i, 0) * 0.4s);
}
.mn-chat__tool-name { color: #6ee7b7; }
.mn-chat__tool-result { color: #34d399; margin-left: 0.5rem; }
.mn-chat__answer {
    margin-top: 0.9rem;
    color: #a1a1aa;
    opacity: 0;
    animation: mn-tool-in 0.5s ease forwards;
    /* --mn-tool-count (set on .mn-chat__window) puts the answer exactly one stagger-step past
       the last tool row — base + step*(count-1) + step == base + step*count — so it always
       lands right as the last row's own 0.4s animation finishes, however many rows there are.
       At count 0 this collapses to the flat 0.5s base delay: the same entrance pause the first
       tool row would have used, not a wait on rows that were never rendered. */
    animation-delay: calc(0.5s + var(--mn-tool-count, 0) * 0.4s);
}
.mn-chat__wikilink { color: #6ee7b7; border-bottom: 1px dashed rgb(110 231 183 / 0.4); }
.mn-chat__caret { display: inline-block; width: 0.5ch; height: 1em; background: #34d399; vertical-align: text-bottom; animation: mn-caret 1s steps(2) infinite; }
.mn-chat--muted .mn-chat__answer { color: #71717a; }
.mn-chat--muted .mn-chat__tool { display: none; }

@keyframes mn-tool-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@keyframes mn-caret { 0%, 50% { opacity: 1; } 51%, 100% { opacity: 0; } }

/* Final frame, no motion — JS-driven variant (marketing-anim.js sets .mn-reduced once it has
   detected prefers-reduced-motion and finished its own reveal bookkeeping). */
.mn-reduced .mn-chat__tool,
.mn-reduced .mn-chat__answer { opacity: 1; animation: none; }
.mn-reduced .mn-chat__caret { display: none; }

/* Same final frame, but keyed directly off the media query so a visitor with OS-level reduced
   motion who never runs marketing-anim.js (blocked, slow, disabled) still gets it — matches the
   JS-independent .fade-up fallback above instead of depending on script execution. */
@media (prefers-reduced-motion: reduce) {
    .mn-chat__tool,
    .mn-chat__answer { opacity: 1; animation: none; }
    .mn-chat__caret { display: none; }
}

.mn-marquee { display: flex; overflow: hidden; gap: 2.5rem; mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); }
.mn-marquee__track { display: flex; gap: 2.5rem; flex-shrink: 0; align-items: center; animation: mn-marquee 40s linear infinite; }
.mn-marquee:hover .mn-marquee__track { animation-play-state: paused; }
.mn-marquee__item { display: inline-flex; align-items: center; gap: 0.5rem; white-space: nowrap; font-size: 0.8rem; color: #a1a1aa; }
.mn-marquee__logo { height: 1.1rem; width: 1.1rem; opacity: 0.75; }
@keyframes mn-marquee { from { transform: translateX(0); } to { transform: translateX(calc(-100% - 2.5rem)); } }

/* Final frame, no motion — JS-driven variant (marketing-anim.js sets .mn-reduced once it has
   detected prefers-reduced-motion and finished its own reveal bookkeeping). */
.mn-reduced .mn-marquee__track { animation: none; }
.mn-reduced .mn-marquee { flex-wrap: wrap; mask-image: none; }

/* Same final frame, but keyed directly off the media query so a visitor with OS-level reduced
   motion who never runs marketing-anim.js (blocked, slow, disabled) still gets it — matches the
   JS-independent .fade-up / .mn-chat fallbacks above instead of depending on script execution.
   The duplicate track is aria-hidden but still painted, so it must stop scrolling and wrap here
   too, not just be hidden from assistive tech. */
@media (prefers-reduced-motion: reduce) {
    .mn-marquee__track { animation: none; }
    .mn-marquee { flex-wrap: wrap; mask-image: none; }
}

.mn-slider__row { display: grid; gap: 1.5rem; }
@media (min-width: 640px) { .mn-slider__row { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.mn-slider__card {
    position: relative; overflow: hidden; text-align: left; width: 100%;
    border: 1px solid rgb(39 39 42 / 0.6); background: rgb(24 24 27 / 0.4);
    border-radius: 0.75rem; padding: 1.5rem; cursor: pointer;
    transition: border-color 0.3s ease, background 0.3s ease;
}
.mn-slider__card[aria-current="true"] { border-color: rgb(16 185 129 / 0.4); background: rgb(16 185 129 / 0.04); }
.mn-slider__card:focus-visible { outline: 2px solid #34d399; outline-offset: 2px; }
/* .mn-slider__card also carries .fade-up for its entrance reveal (see the shared
   .mn-js .fade-up rule near the top of this file). `transition` is a shorthand, so
   whichever selector wins the cascade replaces the whole property, not just the parts
   it mentions — and `.mn-js .fade-up` (two classes) is more specific than the plain
   `.mn-slider__card` rule above, so without this override the entrance-reveal
   transition would silently swallow the border-color/background highlight transition
   the moment `.mn-js` is set (i.e. almost always). Restate all four properties under a
   selector specific enough to win outright, so both transitions keep working together. */
.mn-js .fade-up.mn-slider__card {
    transition:
        opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1) var(--mn-delay, 0ms),
        transform 0.6s cubic-bezier(0.16, 1, 0.3, 1) var(--mn-delay, 0ms),
        border-color 0.3s ease,
        background 0.3s ease;
}
.mn-slider__label { font-family: 'JetBrains Mono', monospace; font-size: 0.7rem; color: #34d399; }
.mn-slider__title { font-size: 1rem; font-weight: 600; color: #f4f4f5; margin: 0.5rem 0 0.4rem; }
.mn-slider__body { font-size: 0.875rem; color: #71717a; line-height: 1.6; }
.mn-slider__graphic { height: 8rem; margin-bottom: 1rem; }
.mn-slider__progress { position: absolute; left: 0; bottom: 0; height: 2px; width: 0; background: #34d399; }
/* Gated on .visible, not just [aria-current="true"]: a CSS animation on a width:0 bar
   starts the instant the node exists, regardless of opacity — it does not wait for the
   card to actually be seen. Stage 0 is server-rendered with aria-current="true", and
   "How it works" sits below the fold behind .fade-up, so an ungated bar would run its
   full --mn-loop and land on "forwards" (fully filled, stopped) before a visitor who
   takes longer than that to scroll down ever sees the card reveal — the bar would then
   be reporting a stage that never actually ran. .visible is added by the same reveal
   machinery (marketing-anim.js's initReveals) that the slider driver's own
   IntersectionObserver roughly tracks before calling start(), so gating here keeps the
   bar's start in step with the auto-advance timer's start instead of firing at first
   paint. Width stays 0 (invisible, not a stuck empty sliver) for any card that is
   [aria-current="true"] but not yet .visible — including the entire no-JS path, where
   .visible is never added at all. */
.mn-slider__card.visible[aria-current="true"] .mn-slider__progress { animation: mn-seg-fill var(--mn-loop, 7500ms) linear forwards; }
@keyframes mn-seg-fill { from { width: 0; } to { width: 100%; } }

/* Final frame, no motion — JS-driven variant (marketing-anim.js sets .mn-reduced once it has
   detected prefers-reduced-motion and finished its own reveal bookkeeping). All three stage
   cards render in the grid simultaneously regardless of motion state — reduced motion only
   turns off the auto-advance and the progress-bar sweep, it never hides a stage. */
.mn-reduced .mn-slider__card { border-color: rgb(16 185 129 / 0.4); }
.mn-reduced .mn-slider__progress { display: none; }

/* Same final frame, but keyed directly off the media query so a visitor with OS-level reduced
   motion who never runs marketing-anim.js (blocked, slow, disabled) still gets it — matches the
   JS-independent .fade-up / .mn-chat / .mn-marquee fallbacks above instead of depending on
   script execution. Every stage stays visible and readable; only the highlight/animation
   differs, so no card is ever left visually suppressed. */
@media (prefers-reduced-motion: reduce) {
    .mn-slider__card { border-color: rgb(16 185 129 / 0.4); }
    .mn-slider__progress { display: none; }
}

.mn-graph { width: 100%; height: 100%; }

/* Hidden-by-default is opt-in, same pattern as .mn-js .fade-up near the top of this file:
   gated behind .mn-js (added by marketing-anim.js as its first statement) so a page where
   the script never loads or never runs renders the graph fully drawn (opacity 1, edges
   already at their final stroke-dashoffset) instead of stuck hidden forever — nothing
   besides the .mn-slider__card.visible reveal below would ever release that state. */
.mn-js .mn-graph__node { opacity: 0; }
.mn-js .mn-graph__edge { stroke-dasharray: 80; stroke-dashoffset: 80; }

/* The draw-in animation is gated one level further, on the card's own .visible class —
   same reasoning as .mn-slider__card.visible[aria-current="true"] .mn-slider__progress
   above: a CSS animation on an opacity:0 node starts the instant the node exists, not when
   the card is actually scrolled into view, so an ungated animation would already be
   finished by the time a below-the-fold "How it works" visitor sees the card reveal.
   nth-child (not :nth-of-type) is safe here — unlike the Task 5 stagger bug, where
   :nth-of-type silently mis-numbered rows because unrelated same-tag siblings shared the
   parent — because .mn-graph__node is the only element inside <g class="mn-graph__nodes">
   and .mn-graph__edge is the only element inside <g class="mn-graph__edges"> (see
   WikiGraph.razor): each group's :nth-child count agrees exactly with its :nth-of-type
   count, so there is nothing for it to mis-number. */
.mn-slider__card.visible .mn-graph__node { animation: mn-node-in 0.5s ease forwards; }
.mn-slider__card.visible .mn-graph__node:nth-child(1) { animation-delay: 0.1s; }
.mn-slider__card.visible .mn-graph__node:nth-child(2) { animation-delay: 0.5s; }
.mn-slider__card.visible .mn-graph__node:nth-child(3) { animation-delay: 0.9s; }
.mn-slider__card.visible .mn-graph__node:nth-child(4) { animation-delay: 1.3s; }
.mn-slider__card.visible .mn-graph__node:nth-child(5) { animation-delay: 1.7s; }

.mn-slider__card.visible .mn-graph__edge { animation: mn-edge-draw 0.7s ease forwards; }
.mn-slider__card.visible .mn-graph__edge:nth-child(1) { animation-delay: 0.6s; }
.mn-slider__card.visible .mn-graph__edge:nth-child(2) { animation-delay: 1.0s; }
.mn-slider__card.visible .mn-graph__edge:nth-child(3) { animation-delay: 1.4s; }
.mn-slider__card.visible .mn-graph__edge:nth-child(4) { animation-delay: 1.8s; }
.mn-slider__card.visible .mn-graph__edge:nth-child(5) { animation-delay: 2.2s; }

@keyframes mn-node-in { from { opacity: 0; transform: scale(0.3); } to { opacity: 1; transform: none; } }
@keyframes mn-edge-draw { to { stroke-dashoffset: 0; } }

/* Final frame, no motion — JS-driven variant (marketing-anim.js sets .mn-reduced once it has
   detected prefers-reduced-motion and finished its own reveal bookkeeping; the same pass
   also adds .visible to every .fade-up immediately, so both classes are present together).
   The extra .mn-slider__card.visible-qualified selectors below out-specificity the
   .mn-slider__card.visible animation rules above outright, rather than leaning on the
   animation-duration:0.001ms !important fallback near the top of this file — that fallback
   would still land on the same "to" keyframe, but stating the end state directly here is
   the same explicit belt-and-suspenders style already used for .mn-chat__tool and
   .mn-marquee__track. */
.mn-reduced .mn-graph__node { opacity: 1; animation: none; }
.mn-reduced .mn-graph__edge { stroke-dashoffset: 0; animation: none; }
.mn-reduced .mn-slider__card.visible .mn-graph__node { opacity: 1; animation: none; }
.mn-reduced .mn-slider__card.visible .mn-graph__edge { stroke-dashoffset: 0; animation: none; }

/* Same final frame, but keyed directly off the media query so a visitor with OS-level
   reduced motion who never runs marketing-anim.js (blocked, slow, disabled) still gets it —
   matches the JS-independent .fade-up / .mn-chat / .mn-marquee / .mn-slider__card fallbacks
   above instead of depending on script execution. Unqualified by .mn-js or .visible, so it
   also wins outright over the hidden-by-default .mn-js rules above regardless of whether
   JS ever ran. */
@media (prefers-reduced-motion: reduce) {
    .mn-graph__node { opacity: 1; animation: none; }
    .mn-graph__edge { stroke-dashoffset: 0; animation: none; }
}

.mn-stage-sources, .mn-stage-clients { display: flex; flex-direction: column; gap: 0.5rem; justify-content: center; height: 100%; }
.mn-stage-row, .mn-stage-pill {
    font-family: 'JetBrains Mono', monospace; font-size: 0.7rem; color: #a1a1aa;
    border: 1px solid rgb(39 39 42 / 0.8); border-radius: 0.375rem;
    padding: 0.3rem 0.6rem;
}
.mn-stage-pill { border-color: rgb(16 185 129 / 0.35); color: #6ee7b7; }

/* These two rows/pills sit in the same slider as .mn-graph and are exposed to the same
   bug: hidden-by-default behind .mn-js (see the .mn-graph__node comment above), and the
   draw-in animation itself gated one level further on the ancestor card's .visible class
   (same precedent as .mn-slider__progress) instead of firing the instant the row mounts.
   .mn-stage-row is the only element inside .mn-stage-sources and .mn-stage-pill the only
   element inside .mn-stage-clients (see Landing.razor's HowItWorksGraphics), so nth-child
   here agrees with nth-of-type and isn't the Task 5 stagger trap. */
.mn-js .mn-stage-row, .mn-js .mn-stage-pill { opacity: 0; }
.mn-slider__card.visible .mn-stage-row,
.mn-slider__card.visible .mn-stage-pill { animation: mn-tool-in 0.5s ease forwards; }
.mn-slider__card.visible .mn-stage-row:nth-child(1), .mn-slider__card.visible .mn-stage-pill:nth-child(1) { animation-delay: 0.2s; }
.mn-slider__card.visible .mn-stage-row:nth-child(2), .mn-slider__card.visible .mn-stage-pill:nth-child(2) { animation-delay: 0.6s; }
.mn-slider__card.visible .mn-stage-row:nth-child(3), .mn-slider__card.visible .mn-stage-pill:nth-child(3) { animation-delay: 1.0s; }

.mn-reduced .mn-stage-row, .mn-reduced .mn-stage-pill { opacity: 1; animation: none; }
.mn-reduced .mn-slider__card.visible .mn-stage-row,
.mn-reduced .mn-slider__card.visible .mn-stage-pill { opacity: 1; animation: none; }
@media (prefers-reduced-motion: reduce) {
    .mn-stage-row, .mn-stage-pill { opacity: 1; animation: none; }
}

.mn-faq__item { border-bottom: 1px solid rgb(39 39 42 / 0.6); }
.mn-faq__q { cursor: pointer; padding: 1rem 0; color: #e4e4e7; font-size: 0.95rem; list-style: none; }
.mn-faq__q::-webkit-details-marker { display: none; }
.mn-faq__q::after { content: '+'; float: right; color: #34d399; }
.mn-faq__item[open] .mn-faq__q::after { content: '\2212'; }
.mn-faq__a { padding-bottom: 1rem; color: #a1a1aa; font-size: 0.875rem; line-height: 1.7; }
