/**
 * Shared form styles — used by the builder preview AND by the WordPress front end, so the
 * plugin ships a copy of this file. Everything is namespaced under .fr- to avoid colliding
 * with whatever theme the public site is running.
 *
 * Everything themeable is a CSS variable set on .fr-form by FormRender.applyTheme(), so a
 * form's design lives in its settings JSON rather than in this file. The values below are the
 * defaults used when a form sets nothing.
 *
 * Sizes inside the form are in `em`, not `rem`: they have to scale with the form's own base
 * font size, and `rem` is relative to the page root, which we do not control.
 */

.fr-form {
    --fr-font: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    --fr-size: 16px;
    --fr-text: #1e293b;
    --fr-muted: #64748b;
    --fr-bg: transparent;
    /* Input surfaces are variables of their own: on a dark form background, white boxes with
       the form's (light) text colour in them would be unreadable. applyTheme() swaps these. */
    --fr-field-bg: #fff;
    --fr-field-border: #cbd5e1;
    --fr-pad: 0;
    --fr-radius: 0;
    --fr-accent: #2563eb;
    --fr-accent-fg: #fff;
    --fr-cta-bg: var(--fr-accent);
    --fr-cta-color: #fff;
    --fr-cta-radius: 7px;
    --fr-cta-font: var(--fr-font);
    --fr-cta-size: 1em;
    --fr-msg-font: var(--fr-font);
    --fr-msg-size: .84em;
    --fr-msg-right: #166534;
    --fr-msg-wrong: #991b1b;

    max-width: 720px; margin: 0 auto;
    font-family: var(--fr-font);
    font-size: var(--fr-size);
    color: var(--fr-text);
    background: var(--fr-bg);
    padding: var(--fr-pad);
    border-radius: var(--fr-radius);
}
.fr-form *, .fr-form *::before, .fr-form *::after { box-sizing: border-box; }

/* The renderer hides Back/Next/Submit with the `hidden` property. A class selector such as
   .fr-btn { display: inline-block } outranks the browser's own [hidden] { display: none },
   so without this the buttons stay visible however often the JS hides them. */
.fr-form [hidden] { display: none !important; }

.fr-page-title { font-size: 1.35em; font-weight: 700; margin: 0 0 16px; }
/* The gap between one block and the next, and between one answer and the next — both an author
   setting. The fallbacks are what every form used before the setting existed, so a version
   published back then still lays out exactly as it did. */
.fr-block { margin: 0 0 var(--fr-gap, 18px); }

.fr-header { font-weight: 700; line-height: 1.25; margin: 0 0 8px; }
h1.fr-header { font-size: 1.8em; }
h2.fr-header { font-size: 1.4em; }
h3.fr-header { font-size: 1.15em; }

.fr-text { line-height: 1.65; }
.fr-text p:last-child { margin-bottom: 0; }

.fr-image, .fr-video, .fr-embed { display: block; width: 100%; height: auto; border-radius: 8px; }
.fr-embed { aspect-ratio: 16 / 9; border: 0; }
.fr-caption { font-size: .82em; color: var(--fr-muted); margin-top: 6px; }
.fr-placeholder {
    padding: 22px; text-align: center; color: #94a3b8; font-size: .85em;
    border: 1px dashed #cbd5e1; border-radius: 8px; background: #f8fafc;
}
.fr-divider { border: 0; border-top: 1px solid #e2e8f0; margin: 24px 0; }

/* ── fields ── */
.fr-field { display: block; }
/* The gap under the question text. Help text, when there is any, sits inside that gap and keeps
   its own margin above the answers — so a question with help reads as label · gap · help · 6px. */
.fr-label { display: block; font-weight: 600; font-size: .92em; margin-bottom: var(--fr-label-gap, 4px); }
.fr-req { color: #dc2626; }
.fr-help { font-size: .82em; color: var(--fr-muted); margin-bottom: 6px; }

.fr-input {
    display: block; width: 100%; padding: 9px 11px;
    font: inherit; color: var(--fr-text); background: var(--fr-field-bg);
    border: 1px solid var(--fr-field-border); border-radius: 7px;
}
.fr-input:focus { outline: none; border-color: var(--fr-accent); box-shadow: 0 0 0 3px rgba(37,99,235,.15); }
textarea.fr-input { resize: vertical; min-height: 96px; }

.fr-choices { display: flex; flex-direction: column; gap: var(--fr-answer-gap, 8px); }
.fr-choice { display: flex; align-items: center; gap: 9px; font-size: .93em; cursor: pointer; }
.fr-choice input { width: 17px; height: 17px; accent-color: var(--fr-accent); flex: none; }

/* Single choice: the whole option is the control — the answer itself is the button.
   Options flow along a row and wrap only once they stop fitting, so True / False sits side by
   side while wordier answers take a line each. Each one is sized from its own text, so the
   layout follows what the author wrote rather than a guessed breakpoint.
   The radio itself stays in the DOM (hidden, but focusable) so keyboard and screen-reader
   users get the real thing; .fr-on is toggled by the renderer. */
.fr-choices[data-style="button"] { flex-direction: row; flex-wrap: wrap; gap: var(--fr-answer-gap, 10px); }
.fr-choices[data-style="button"] .fr-choice {
    position: relative; justify-content: center; text-align: center;
    /* Grow to share the row. `anywhere` rather than only `break-word`: the answer's text is an
       anonymous flex item, so nothing can set a min-width on it, and only `anywhere` shrinks its
       min-content — without it a 45-character word overflows the button on a phone. Neither
       property moves the break: where a row wraps is decided by the answer's own text width. */
    flex: 1 1 auto; min-width: 0; overflow-wrap: break-word; overflow-wrap: anywhere;
    padding: 13px 18px; font-size: 1em; font-weight: 600; line-height: 1.3;
    color: var(--fr-text); background: var(--fr-field-bg);
    border: 1.5px solid var(--fr-field-border); border-radius: var(--fr-cta-radius);
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.fr-choices[data-style="button"] .fr-choice input {
    position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; pointer-events: none;
}
/* An animated answer needs BOTH transitions. The rule above is more specific than
   `.fr-form [data-anim]`, so on its own it replaces the reveal transition entirely — the option
   then jumps into view with no fade and, worse, no stagger delay, because the delay lives in the
   transition it just lost. That is why answers appeared all at once while the label animated. */
.fr-form .fr-choices[data-style="button"] .fr-choice[data-anim] {
    transition: opacity var(--fr-duration, 450ms) ease var(--fr-delay, 0ms),
                transform var(--fr-duration, 450ms) ease var(--fr-delay, 0ms),
                filter var(--fr-duration, 450ms) ease var(--fr-delay, 0ms),
                background .15s ease, border-color .15s ease, color .15s ease;
}
.fr-choices[data-style="button"] .fr-choice:hover { border-color: var(--fr-accent); }
.fr-choices[data-style="button"] .fr-choice.fr-focus { box-shadow: 0 0 0 3px rgba(37,99,235,.2); }
.fr-choices[data-style="button"] .fr-choice.fr-on {
    color: var(--fr-accent-fg); background: var(--fr-accent); border-color: var(--fr-accent);
}

.fr-error { color: #dc2626; font-size: .82em; margin-top: 4px; min-height: 0; }
.fr-invalid .fr-input, .fr-invalid .fr-choices { border-color: #dc2626; }
/* a tint rather than a fixed colour, so it also works on a dark form background */
.fr-invalid .fr-input { box-shadow: inset 0 0 0 999px rgba(220, 38, 38, .07); }
/* the list itself has no border in button mode, so mark the options instead */
.fr-invalid .fr-choices[data-style="button"] .fr-choice { border-color: #dc2626; }

/* quiz feedback, filled in per answer when enabled */
.fr-feedback {
    font-family: var(--fr-msg-font); font-size: var(--fr-msg-size);
    font-weight: 600; margin-top: 4px;
}
.fr-feedback.fr-right { color: var(--fr-msg-right); }
.fr-feedback.fr-wrong { color: var(--fr-msg-wrong); }

/* ── buttons / nav ──
   The CTA is themeable; Back/Next/Submit follow it so the form looks like one thing. */
.fr-cta, .fr-btn {
    display: inline-block; padding: 11px 22px;
    font-family: var(--fr-cta-font); font-size: var(--fr-cta-size); font-weight: 700;
    color: var(--fr-cta-color); background: var(--fr-cta-bg); border: none;
    border-radius: var(--fr-cta-radius);
    cursor: pointer; text-decoration: none; text-align: center;
}
.fr-cta:hover, .fr-btn:hover { filter: brightness(.92); }
.fr-btn.fr-back { background: var(--fr-field-bg); color: var(--fr-muted); border: 1px solid var(--fr-field-border); }
.fr-btn.fr-back:hover { filter: brightness(.95); }
.fr-btn:disabled { opacity: .6; cursor: default; }
.fr-nav { display: flex; gap: 10px; align-items: center; margin-top: 22px; }

.fr-status { margin-top: 12px; font-size: .9em; color: var(--fr-muted); }
.fr-status.fr-ok  { color: #166534; }
.fr-status.fr-err { color: #b91c1c; }

.fr-done { padding: 26px 22px; text-align: center; background: #f0fdf4;
           border: 1px solid #86efac; border-radius: 10px; }
.fr-done .fr-score { font-weight: 700; font-size: 1.1em; margin-top: 8px; }
/* An author-built confirmation brings its own design, so the green card gets out of the way. */
.fr-done.fr-done-blocks { padding: 0; background: none; border: 0; text-align: left; }

/* ── reveal animations ──
   Elements with data-anim start hidden and are revealed by IntersectionObserver adding
   .fr-shown. Delay/duration come from the block's own settings via CSS variables.
   A [data-anim-group] block is not animated itself — its parts are, on a stagger. */
.fr-form [data-anim] {
    opacity: 0;
    transition: opacity var(--fr-duration, 450ms) ease var(--fr-delay, 0ms),
                transform var(--fr-duration, 450ms) ease var(--fr-delay, 0ms),
                filter var(--fr-duration, 450ms) ease var(--fr-delay, 0ms);
    will-change: opacity, transform;
}
.fr-form [data-anim="fade"]        { transform: none; }
.fr-form [data-anim="slide-up"]    { transform: translateY(24px); }
.fr-form [data-anim="slide-down"]  { transform: translateY(-24px); }
.fr-form [data-anim="slide-in"]    { transform: translateX(-24px); }
.fr-form [data-anim="slide-right"] { transform: translateX(24px); }
.fr-form [data-anim="zoom"]        { transform: scale(.94); }
.fr-form [data-anim="zoom-out"]    { transform: scale(1.06); }
.fr-form [data-anim="blur"]        { transform: none; filter: blur(9px); }
.fr-form [data-anim="rise"]        { transform: translateY(24px) scale(.97); }
.fr-form [data-anim="flip"]        { transform: perspective(800px) rotateX(-32deg);
                                     transform-origin: top center; }
/* overshoots slightly on the way in, which is what makes it read as a "pop" */
.fr-form [data-anim="pop"] {
    transform: scale(.82);
    transition-timing-function: cubic-bezier(.34, 1.56, .64, 1);
}

/* ── keyframed reveals ──
   The effects above move once, from one state to another, which is exactly what a transition
   does. These three cross the same point several times — a bounce, a shake, three turns — so
   they are keyframes instead.
   The transition has to be switched off for them: a running transition outranks an animation in
   the cascade, so leaving it on would quietly flatten the motion back into a straight slide.
   Button answers re-declare their own transition (see the top of this file), which is why they
   need saying again here; they keep the colour transition, since that belongs to hover and
   selection rather than to the reveal.

   That second rule must write the whole `transition`, not just transition-property. Narrowing
   the property list alone leaves the six durations and six delays from the rule above in place,
   and they re-index onto whatever three properties are left: `background` then inherited the
   reveal's 450ms and — the visible bug — its delay, so an answer set to arrive 1.5s late took
   1.5s to highlight when it was clicked. */
.fr-form [data-anim="grand"],
.fr-form [data-anim="shake"],
.fr-form [data-anim="spin"],
.fr-form [data-anim="typing"] { transition-property: none; }
.fr-form .fr-choices[data-style="button"] .fr-choice[data-anim="grand"],
.fr-form .fr-choices[data-style="button"] .fr-choice[data-anim="shake"],
.fr-form .fr-choices[data-style="button"] .fr-choice[data-anim="spin"],
.fr-form .fr-choices[data-style="button"] .fr-choice[data-anim="typing"] {
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.fr-form [data-anim="grand"].fr-shown {
    animation: fr-grand var(--fr-duration, 850ms) var(--fr-delay, 0ms) both;
}
.fr-form [data-anim="shake"].fr-shown {
    animation: fr-shake var(--fr-duration, 650ms) var(--fr-delay, 0ms) both;
}
.fr-form [data-anim="spin"].fr-shown {
    /* fast at first, then long and slow into place */
    animation: fr-spin var(--fr-duration, 900ms) var(--fr-delay, 0ms) both cubic-bezier(.06, .8, .16, 1);
}

/* Flies up from below and settles. --fr-travel is measured at reveal time as the distance to the
   bottom of the form, so it really does come from the bottom of the form rather than from a
   fixed guess; the fallback covers a message written straight into the page. */
@keyframes fr-grand {
    0%   { opacity: 0; transform: translateY(var(--fr-travel, 300px));
           animation-timing-function: cubic-bezier(.16, .85, .3, 1); }   /* the fast part */
    50%  { opacity: 1; transform: translateY(-18px); animation-timing-function: ease-in; }
    68%  { transform: translateY(0);  animation-timing-function: ease-out; }
    82%  { transform: translateY(-8px); animation-timing-function: ease-in; }
    92%  { transform: translateY(0);  animation-timing-function: ease-out; }
    97%  { transform: translateY(-2px); }
    100% { opacity: 1; transform: translateY(0); }
}

/* Three passes left and right, each a little smaller, then still. */
@keyframes fr-shake {
    0%   { opacity: 0; transform: translateX(0); }
    12%  { opacity: 1; transform: translateX(-11px); }
    28%  { transform: translateX(11px); }
    44%  { transform: translateX(-9px); }
    60%  { transform: translateX(9px); }
    76%  { transform: translateX(-5px); }
    90%  { transform: translateX(4px); }
    100% { opacity: 1; transform: translateX(0); }
}

/* Three turns about its own middle, unwinding into place. */
@keyframes fr-spin {
    0%   { opacity: 0; transform: rotate(1080deg) scale(.55); }
    100% { opacity: 1; transform: rotate(0deg) scale(1); }
}

/* "Typing" is written out by the renderer rather than animated here — text arriving one letter
   at a time is not something a keyframe can express. All the CSS does is the caret that blinks
   while it is being typed; the class comes off when the last letter lands. */
.fr-form .fr-typing::after {
    content: ''; display: inline-block; vertical-align: -.12em;
    width: .08em; height: 1em; margin-left: .06em;
    background: currentColor;
    animation: fr-caret .9s steps(1, end) infinite;
}
@keyframes fr-caret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

.fr-form [data-anim].fr-shown { opacity: 1; transform: none; filter: none; width: fit-content; }

/* Used for a single frame while a message's start state is put in place. Without it, applying
   [data-anim] starts a transition TOWARDS the hidden state, which the reveal then immediately
   reverses — so the message hardly moves. Only transition-property is switched off, so the
   duration and easing can still be read back and handed to the real animation. */
.fr-form .fr-instant { transition-property: none !important; }

/* Never animate for someone who has asked not to see motion — and make sure the content is
   visible regardless, so a missing observer can't leave the form blank. */
@media (prefers-reduced-motion: reduce) {
    .fr-form [data-anim] { opacity: 1 !important; transform: none !important; transition: none !important;
                           animation: none !important; }
}

/* A section switched off by a condition (show_if). */
.fr-block[hidden] { display: none !important; }
