/* Data Visualisation block — frontend.
 *
 * Container shell mirrors the field shell used elsewhere in krskrt-forms:
 *   1px #dedede border · #fff background · 5px border-radius
 */

.kf-viz {
    position:       relative;
    box-sizing:     border-box;
    border:         1px solid #dedede;
    background:     #fff;
    border-radius:  5px;
    padding:        12px;
    margin:         0 auto;
    overflow:       hidden;
}

.kf-viz.is-unconfigured,
.kf-viz.is-error {
    color:       #aaa;
    font-style:  italic;
    text-align:  center;
}

.kf-viz-canvas {
    position: relative;
    width:    100%;
    height:   100%;
    min-height: 120px;
}

/* ------------------------------------------------------------------
 * Phrase cloud (pure CSS — no canvas, crisp at any DPI)
 *
 * Shared between the literal-frequency Word cloud (`vizType: wordcloud`)
 * and the AI word cloud (`vizType: aicloud`). Both emit the same
 * `.kf-viz-canvas > .kf-viz-tag` markup, so they share layout.
 * ----------------------------------------------------------------- */
.kf-viz--wordcloud .kf-viz-canvas,
.kf-viz--aicloud   .kf-viz-canvas {
    display:        flex;
    flex-wrap:      wrap;
    align-content:  center;      /* centre rows when few tags; JS fitting
                                    ensures we never overflow              */
    align-items:    center;
    justify-content:center;
    gap:            6px 12px;
    height:         100%;
    min-height:     0;           /* override base min-height:120px         */
    overflow:       hidden;      /* clip sub-pixel dust; JS fit does the rest */
    padding:        8px;
    box-sizing:     border-box;
}

.kf-viz-tag {
    display:     inline-block;
    /* Falls back to the bold-by-default look. The viz block's "Viz font
       weight" Typography control overrides this by setting
       `--kf-viz-font-weight` inline on the .kf-viz wrap (see
       templates/blocks/data-viz.php). Inheritance alone isn't enough
       here: this explicit `.kf-viz-tag` rule would otherwise beat the
       wrap's inherited weight. */
    font-weight: var(--kf-viz-font-weight, 700);
    line-height: 1;
    white-space: normal;     /* allow long phrases to wrap at spaces    */
    word-break:  break-word; /* break runs with no spaces               */
    max-width:   100%;       /* never push past the container edge      */
    text-align:  center;     /* centre text within a wrapped tag        */
    text-wrap:   balance;    /* equalise line lengths — no ragged edges */
    cursor:      default;
    transition:  opacity 0.3s ease;
}

/* "…" rest-bucket entry — collects responses whose content didn't
 * surface in any other cluster. Rendered INLINE in the cloud flow
 * (just another tag, but with border-only chrome) so it reads as
 * the "and more" marker at the end of the row. Visible on every
 * surface, but only INTERACTIVE inside the fullscreen modal — the
 * inline cloud is read-only by design. */
.kf-viz-tag.kf-viz-tag--ellipsis {
    background:      transparent;
    border:          2px solid currentColor;
    border-radius:   5px;
    padding:         1px 10px;
    font-size:       22px !important;  /* drawCloud sets fontSize inline */
    line-height:     1;
    letter-spacing:  2px;
    cursor:          default;
    pointer-events:  none;
    transition:      opacity 0.2s ease, transform 0.15s ease;
}
/* Only the fullscreen-modal copy is clickable. */
.kf-viz-modal .kf-viz-tag.kf-viz-tag--ellipsis {
    cursor:         pointer;
    pointer-events: auto;
}
.kf-viz-modal .kf-viz-tag.kf-viz-tag--ellipsis:hover,
.kf-viz-modal .kf-viz-tag.kf-viz-tag--ellipsis:focus-visible {
    transform:      translateY(-1px);
    opacity:        0.85;
}

/* ------------------------------------------------------------------
 * Text masonry
 *  - Explicit per-column DOM (.kf-viz-col) so new items prepend into
 *    the shortest column and existing items animate downward via FLIP.
 *  - Items are line-clamped with ellipsis when they exceed the
 *    configured "Max lines per item".
 *  - Item border colour cycles through the configured palette by
 *    insertion order (set inline by JS).
 * ----------------------------------------------------------------- */
.kf-viz--masonry .kf-viz-canvas {
    overflow-x:     hidden;
    overflow-y:     auto;
    max-height:     100%;
    height:         100%;
    box-sizing:     border-box;
}
.kf-viz-masonry-grid {
    display:        grid;
    grid-template-columns: repeat(var(--kf-cols, 3), minmax(0, 1fr));
    gap:            0 8px;
    align-items:    start;
}
.kf-viz-col {
    display:        flex;
    flex-direction: column;
    min-width:      0;
}
.kf-viz--masonry .kf-viz-item {
    position:               relative;
    cursor:                 pointer;
    background:             #fff;
    border:                 1px solid #dedede;
    border-radius:          5px;
    padding:                8px 10px;
    margin:                 0 0 8px;
    color:                  #00374a;
    font-size:              14px;
    line-height:            1.35;
    /* Hard clip — max-height guarantees no overflow text leaks past the
     * clamp on every browser. We do NOT use -webkit-line-clamp here so
     * the native (text-coloured) ellipsis is suppressed; a colored
     * ::after overlay replaces it via the .is-clamped class set in JS. */
    max-height:             calc(1.35em * var(--kf-clamp, 4));
    overflow:               hidden;
    word-break:             break-word;
    transition:             box-shadow 0.2s ease;
}
/* Inline ellipsis appended into the text after JS truncates to the
 * longest prefix that fits. Sits flush against the last visible word
 * (no gap, no overlay) and inherits the item's border colour from the
 * inline `style="color:…"` set in JS. */
.kf-viz-item-ellipsis {
    font-weight:  700;
    margin-left:  1px;
    pointer-events: none;
}
.kf-viz--masonry .kf-viz-item.is-new {
    animation: kfVizFadeIn 0.4s ease-out both, kfVizPulse 1.6s ease-out 0.4s 1;
}
@keyframes kfVizFadeIn {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: translateY(0); }
}
@keyframes kfVizPulse {
    0%   { box-shadow: 0 0 0 0   var(--kf-border-rgba, rgba(0,55,74,0.45)); }
    60%  { box-shadow: 0 0 0 6px rgba(0,0,0,0); }
    100% { box-shadow: 0 0 0 0   rgba(0,0,0,0); }
}

/* ------------------------------------------------------------------
 * Repeater "rows" (table) — vizType: rows
 *
 * Each repeater entry = one .kf-viz-row; included sub-fields are columns
 * aligned via a shared grid template (repeat(N, 1fr)). A .kf-viz-rows-head
 * carries the column headings. Each row gets ONE coloured border (all four
 * sides) cycling the palette — set inline by JS via border-color /
 * --kf-border-color. Cells clamp to "Max lines per item" using the same
 * hard-clip + JS ellipsis approach as the masonry items.
 * ----------------------------------------------------------------- */
.kf-viz--rows .kf-viz-canvas {
    overflow-x: hidden;
    overflow-y: auto;
    max-height: 100%;
    height:     100%;
    box-sizing: border-box;
}
.kf-viz-rows-grid {
    display:        flex;
    flex-direction: column;
    gap:            8px;
}
.kf-viz-rows-head {
    display:               grid;
    grid-template-columns: repeat(var(--kf-cols, 1), minmax(0, 1fr));
    gap:                   0 10px;
    padding:               0 12px;
}
.kf-viz-rows-th {
    font-family:    var(--kf-rows-th-family, inherit);
    font-weight:    var(--kf-rows-th-weight, var(--kf-viz-font-weight, 700));
    font-size:      13px;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    color:          #00374a;
    min-width:      0;
    word-break:     break-word;
}
.kf-viz-row {
    display:               grid;
    grid-template-columns: repeat(var(--kf-cols, 1), minmax(0, 1fr));
    gap:                   0 10px;
    align-items:           start;
    cursor:                pointer;
    background:            #fff;
    border:                1px solid #dedede;   /* border-color set inline (palette) */
    border-radius:         5px;
    padding:               8px 10px;
    transition:            box-shadow 0.2s ease;
}
.kf-viz-rows-cell {
    position:    relative;
    color:       #00374a;
    font-size:   14px;
    line-height: 1.35;
    min-width:   0;
    max-height:  calc(1.35em * var(--kf-clamp, 4));
    overflow:    hidden;
    word-break:  break-word;
    white-space: pre-wrap;
}
.kf-viz-row.is-new {
    animation: kfVizFadeIn 0.4s ease-out both, kfVizPulse 1.6s ease-out 0.4s 1;
}
/* Bare + overflow depth shadow (mirror masonry). */
.kf-viz--bare.kf-viz--rows .kf-viz-canvas.is-overflowing {
    border-bottom: 1px solid #dedede;
}
.kf-viz--bare.kf-viz--rows:has(.kf-viz-canvas.is-overflowing)::after {
    content:        '';
    position:       absolute;
    left:           0;
    right:          0;
    bottom:         0;
    height:         14px;
    box-shadow:     inset 0 -10px 10px -10px rgba(0, 0, 0, 0.18);
    pointer-events: none;
    z-index:        5;
}
/* Per-row zoom — overlay reuses .kf-viz-zoom / .kf-viz-zoom-close. */
.kf-viz-row-zoom-card {
    position:       relative;
    max-width:      min(900px, 86vw);
    max-height:     84vh;
    overflow:       auto;
    background:     #fff;
    color:          #00374a;
    border:         4px solid #00374a;   /* overridden inline with the row colour */
    border-radius:  8px;
    padding:        32px 40px;
    display:        flex;
    flex-direction: column;
    gap:            18px;
}
.kf-viz-row-zoom-label {
    font-weight:    var(--kf-viz-font-weight, 700);
    font-size:      13px;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    color:          #6b7280;
    margin-bottom:  4px;
}
.kf-viz-row-zoom-value {
    font-size:   24px;
    line-height: 1.4;
    white-space: pre-wrap;
    word-break:  break-word;
}

/* ------------------------------------------------------------------
 * Expand-to-modal hint button — same shape as .krskrt-image-field__hint.
 * Top-right of the masonry; fades in on container hover.
 * ----------------------------------------------------------------- */
/* Shared chrome for the three aicloud / viz panel buttons — same look
 * as the floating #krskrt-mm-panel buttons. Stacked vertically in the
 * top-right of the viz wrap. Hidden by default, fade in on hover so
 * they don't compete with the content visually. */
.kf-viz-expand,
.kf-viz-aicloud-refresh,
.kf-viz-aicloud-autopoll {
    position:         absolute;
    right:            8px;
    width:            44px;
    height:           44px;
    padding:          0;
    background:       #fff;
    color:            #00374a;
    border:           1px solid #dedede;
    border-radius:    10px;
    cursor:           pointer;
    display:          flex;
    align-items:      center;
    justify-content:  center;
    font-size:        22px;
    line-height:      1em;
    box-shadow:       0 1px 4px rgba(0, 0, 0, 0.08);
    opacity:          0;
    transition:       opacity 0.15s ease,
                      background 0.15s ease,
                      color 0.15s ease,
                      border-color 0.15s ease;
    z-index:          3;
}
.kf-viz-expand          { top:   8px; }
.kf-viz-aicloud-refresh { top:  60px; }
.kf-viz-aicloud-autopoll{ top: 112px; }

.kf-viz:hover .kf-viz-expand,
.kf-viz:hover .kf-viz-aicloud-refresh,
.kf-viz:hover .kf-viz-aicloud-autopoll,
.kf-viz:hover .kf-viz-stepper-group,
.kf-viz-modal:hover .kf-viz-expand,
.kf-viz-modal:hover .kf-viz-aicloud-refresh,
.kf-viz-modal:hover .kf-viz-aicloud-autopoll,
.kf-viz-expand:focus-visible,
.kf-viz-aicloud-refresh:focus-visible,
.kf-viz-aicloud-autopoll:focus-visible,
.kf-viz-stepper-group:focus-within {
    opacity: 1;
}
.kf-viz-expand:hover,
.kf-viz-aicloud-refresh:hover:not(:disabled),
.kf-viz-aicloud-autopoll:hover:not(:disabled) {
    background:   #00a9bb;
    color:        #00374a;
    border-color: #00a9bb;
}
.kf-viz-expand:active,
.kf-viz-aicloud-refresh:active:not(:disabled),
.kf-viz-aicloud-autopoll:active:not(:disabled) {
    background:   #00262d;
    color:        #fff;
    border-color: #00262d;
}
/* "Paused" indicator for the autopoll button — soft amber tint so the
 * frozen state is obvious without needing hover. */
.kf-viz-aicloud-autopoll[data-state="off"] {
    background:   #fff7ed;
    border-color: #f59e0b;
    color:        #b45309;
}
.kf-viz-aicloud-autopoll[data-state="off"]:hover {
    background:   #fde68a;
}
.kf-viz-expand .ph,
.kf-viz-expand > i,
.kf-viz-aicloud-refresh > i,
.kf-viz-aicloud-autopoll > i {
    display:        block;
    line-height:    1;
    pointer-events: none;
}

/* The cloned kf-viz inside a modal is the "expanded" view, so its
 * own expand button is hidden — but the refresh/autopoll buttons
 * DO stay (they trigger central pipeline actions either way). */
.kf-viz-modal .kf-viz-expand { display: none !important; }
/* Inside the modal, shift refresh + autopoll up to the topmost slot
 * since expand is gone. */
.kf-viz-modal .kf-viz-aicloud-refresh  { top:  8px; }
.kf-viz-modal .kf-viz-aicloud-autopoll { top: 60px; }

/* ------------------------------------------------------------------
 * Hide individual answers (Central screen only)
 *
 * Per-answer 'x' overlay (masonry tiles + repeater rows + AI cloud
 * answer cards) + the block-level "show hidden" button and its
 * review panel. The 'x' fades in on item hover; the panel lists
 * hidden answers each with a restore button.
 * ------------------------------------------------------------------ */
.kf-viz-row { position: relative; }

.kf-viz-item-hide {
    position:        absolute;
    top:             4px;
    right:           4px;
    width:           22px;
    height:          22px;
    padding:         0;
    display:         flex;
    align-items:     center;
    justify-content: center;
    background:      #fff;
    color:           #00374a;
    border:          1px solid #dedede;
    border-radius:   6px;
    cursor:          pointer;
    font-size:       13px;
    line-height:     1;
    box-shadow:      0 1px 3px rgba(0, 0, 0, 0.12);
    opacity:         0;
    transition:      opacity 0.12s ease, background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
    z-index:         4;
}
.kf-viz-item:hover  .kf-viz-item-hide,
.kf-viz-row:hover   .kf-viz-item-hide,
.kf-viz-item-hide:focus-visible {
    opacity: 1;
}
.kf-viz-item-hide:hover {
    background:   #00a9bb;
    color:        #00374a;
    border-color: #00a9bb;
}
.kf-viz-item-hide > i { display: block; line-height: 1; pointer-events: none; }

/* In the masonry modal the canvas is normally height:100%; let it flex
 * instead so the hidden-reveal area can share the column below it. */
.kf-viz-modal.kf-viz--masonry .kf-viz-canvas {
    flex:       1 1 auto;
    height:     auto;
    min-height: 0;
}

/* ── Hidden-answers reveal (inside the maximized modal only) ─────────────
 * A grid-viz (masonry / rows) modal gets a toggle BELOW its content. When
 * pressed it expands a SEPARATE area of hidden-answer cards underneath —
 * never merged into the live grid above. Each card has an eye (restore). */
.kf-viz-hidden-reveal {
    flex:        0 0 auto;
    width:       100%;
    box-sizing:  border-box;
    padding:     10px 24px 16px;
    border-top:  1px dashed #d4d9dd;
}
.kf-viz-hidden-reveal.is-empty { display: none; }
.kf-viz-hidden-toggle {
    display:        inline-flex;
    align-items:    center;
    gap:            8px;
    background:     #fff;
    color:          #00374a;
    border:         1px solid #dedede;
    border-radius:  10px;
    padding:        8px 12px;
    font-size:      14px;
    line-height:    1;
    cursor:         pointer;
    box-shadow:     0 1px 4px rgba(0, 0, 0, 0.08);
    transition:     background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.kf-viz-hidden-toggle:hover { background: #00a9bb; border-color: #00a9bb; }
.kf-viz-hidden-toggle > i { font-size: 18px; line-height: 1; pointer-events: none; }
.kf-viz-hidden-area {
    display:               grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap:                   8px;
    margin-top:            10px;
    max-height:            40vh;
    overflow-y:            auto;
}
.kf-viz-hidden-area.is-collapsed { display: none; }
.kf-viz-hidden-card {
    position:      relative;
    background:    #f6f7f8;
    border:        1px dashed #c4ccd2;
    border-radius: 5px;
    padding:       8px 30px 8px 10px;
    color:         #475569;
    font-size:     13px;
    line-height:   1.35;
    word-break:    break-word;
}
.kf-viz-hidden-restore {
    position:        absolute;
    top:             4px;
    right:           4px;
    width:           22px;
    height:          22px;
    padding:         0;
    display:         flex;
    align-items:     center;
    justify-content: center;
    background:      #fff;
    color:           #0f766e;
    border:          1px solid #dedede;
    border-radius:   6px;
    cursor:          pointer;
    font-size:       13px;
    line-height:     1;
    box-shadow:      0 1px 3px rgba(0, 0, 0, 0.12);
    transition:      background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.kf-viz-hidden-restore:hover { background: #0f766e; color: #fff; border-color: #0f766e; }
.kf-viz-hidden-restore > i { display: block; line-height: 1; pointer-events: none; }

/* AI word cloud: the eye-slash "reveal hidden answers" tag, sitting right
 * after the "…" ellipsis tag in the cloud. Border-only chrome like the
 * ellipsis; clicking opens the hidden-answers popup. */
.kf-viz-tag.kf-viz-tag--hidden-reveal {
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    color:           #0f766e;
    border:          1px dashed #0f766e;
    border-radius:   8px;
    padding:         2px 8px;
    cursor:          pointer;
}
.kf-viz-tag.kf-viz-tag--hidden-reveal:hover { background: rgba(15, 118, 110, 0.08); }
.kf-viz-tag.kf-viz-tag--hidden-reveal > i { font-size: 18px; line-height: 1; pointer-events: none; }

/* AI word cloud popup: restore (eye) button on a hidden-answer card. Mirrors
 * .kf-viz-answer-hide but teal (restore intent). */
.kf-viz-answer-restore {
    position:        absolute;
    top:             4px;
    right:           4px;
    width:           22px;
    height:          22px;
    padding:         0;
    display:         flex;
    align-items:     center;
    justify-content: center;
    background:      #fff;
    color:           #0f766e;
    border:          1px solid #dedede;
    border-radius:   6px;
    cursor:          pointer;
    font-size:       13px;
    line-height:     1;
    box-shadow:      0 1px 3px rgba(0, 0, 0, 0.12);
    opacity:         0;
    transition:      opacity 0.12s ease, background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
    z-index:         2;
}
.kf-viz-mindmap-popup-answer:hover .kf-viz-answer-restore,
.kf-viz-answer-restore:focus-visible { opacity: 1; }
.kf-viz-answer-restore:hover { background: #0f766e; color: #fff; border-color: #0f766e; }
.kf-viz-answer-restore > i { display: block; line-height: 1; pointer-events: none; }

/* Tap-to-zoom on the fullscreen MODAL — driven by the marker's inline
 * preview via the viz_zoom relay message. The canvas inside the modal is
 * scaled + translated to centre the tapped point; the modal itself
 * already clips overflow. */
.kf-viz-modal.kf-viz-is-zoomed .kf-viz-canvas {
    will-change: transform;
}

/* ------------------------------------------------------------------
 * Full-screen masonry modal (Central side) — TRULY fullscreen, no inner
 * padded container. Z-index above .krskrt-mm-panel (max 100000) so the
 * modal sits on top of the floating MM/dataview controls.
 * ----------------------------------------------------------------- */
.kf-viz-modal {
    position:        fixed;
    inset:           0;
    z-index:         100002;
    background:      #fff;
    display:         flex;
    flex-direction:  column;
    padding:         0;
    margin:          0;
    box-sizing:      border-box;
}
.kf-viz-modal[hidden] { display: none; }
/* Generic-type modal (wordcloud / bar-h / bar-v): the inner .kf-viz wrap
 * needs an explicit min-height so the wordcloud/bar renderers have room
 * to lay out at full viewport size. */
.kf-viz-modal--generic > .kf-viz {
    flex:       1 1 auto;
    width:      100%;
    height:     100%;
    min-height: 0;
}
.kf-viz-modal--generic > .kf-viz > .kf-viz-canvas {
    height:     100%;
    max-height: 100%;
}
.kf-viz-modal-close {
    position:        absolute;
    top:             12px;
    right:           12px;
    width:           44px;
    height:          44px;
    border-radius:   50%;
    background:      rgba(0, 55, 74, 0.28);
    color:           #fff;
    border:          0;
    cursor:          pointer;
    font-size:       22px;
    display:         flex;
    align-items:     center;
    justify-content: center;
    z-index:         2;
}
.kf-viz-modal-close:hover { background: rgba(0, 55, 74, 0.42); }
.kf-viz-modal-title {
    flex:            0 0 auto;
    text-align:      center;
    padding:         18px 56px 8px;
    color:           #00374a;
    font-size:       26px;
    font-weight:     700;
    line-height:     1.25;
    word-break:      break-word;
}
/* Full-screen masonry field-filter pills — sit beneath the title. */
.kf-viz-modal-filters {
    flex:            0 0 auto;
    display:         flex;
    flex-wrap:       wrap;
    justify-content: center;
    gap:             8px;
    padding:         4px 56px 12px;
}
.kf-viz-filter-pill {
    font:            inherit;
    font-size:       14px;
    line-height:     1;
    padding:         7px 14px;
    border-radius:   999px;
    border:          1px solid #00374a;
    background:      #fff;
    color:           #00374a;
    cursor:          pointer;
    opacity:         0.45;
    transition:      opacity 0.15s, background 0.15s, color 0.15s;
}
.kf-viz-filter-pill:hover { opacity: 0.75; }
.kf-viz-filter-pill.is-active {
    opacity:    1;
    background: #00374a;
    color:      #fff;
}
.kf-viz-modal .kf-viz-canvas {
    flex:            1 1 auto;
    width:           100%;
    height:          auto;
    max-height:      none;
    background:      #fff;
    border-radius:   0;
    padding:         12px 16px 24px;
    overflow-y:      auto;
    overflow-x:      hidden;
    box-sizing:      border-box;
}

/* ------------------------------------------------------------------
 * Item-zoom modal (Central side; mirrored on the marker via relay).
 * ----------------------------------------------------------------- */
.kf-viz-zoom {
    position:        fixed;
    inset:           0;
    z-index:         100004;
    background:      rgba(15, 15, 26, 0.94);
    display:         flex;
    align-items:     center;
    justify-content: center;
    padding:         24px;
    box-sizing:      border-box;
}
.kf-viz-zoom[hidden] { display: none; }
.kf-viz-zoom-content {
    max-width:       min(900px, 80vw);
    max-height:      80vh;
    overflow:        auto;
    background:      #fff;
    color:           #00374a;
    border:          4px solid var(--kf-zoom-border, #00374a);
    border-radius:   8px;
    padding:         32px 40px;
    font-size:       28px;
    line-height:     1.4;
    white-space:     pre-wrap;
    word-break:      break-word;
}
.kf-viz-zoom-prev,
.kf-viz-zoom-next,
.kf-viz-zoom-close {
    position:        absolute;
    width:           56px;
    height:          56px;
    border-radius:   50%;
    background:      rgba(255, 255, 255, 0.14);
    color:           #fff;
    border:          0;
    cursor:          pointer;
    font-size:       28px;
    display:         flex;
    align-items:     center;
    justify-content: center;
}
.kf-viz-zoom-prev:hover,
.kf-viz-zoom-next:hover,
.kf-viz-zoom-close:hover { background: rgba(255, 255, 255, 0.26); }
.kf-viz-zoom-prev  { left: 24px;  top: 50%; transform: translateY(-50%); }
.kf-viz-zoom-next  { right: 24px; top: 50%; transform: translateY(-50%); }
.kf-viz-zoom-close { top: 16px; right: 16px; }
.kf-viz-zoom-prev[disabled],
.kf-viz-zoom-next[disabled] { opacity: 0.35; cursor: default; }

/* ------------------------------------------------------------------
 * Bar charts (horizontal + vertical)
 * ----------------------------------------------------------------- */
.kf-viz--bar-h .kf-viz-canvas {
    display:        flex;
    flex-direction: column;
    gap:            8px;
    overflow-y:     auto;
    max-height:     100%;
}
.kf-viz--bar-h .kf-viz-bar {
    display:       flex;
    align-items:   center;
    gap:           10px;
    color:         #00374a;
    font-size:     14px;
}
.kf-viz--bar-h .kf-viz-bar-label {
    flex:          0 0 35%;
    text-align:    right;
    overflow:      hidden;
    text-overflow: ellipsis;
    white-space:   nowrap;
}
.kf-viz--bar-h .kf-viz-bar-track {
    flex:          1 1 auto;
    height:        20px;
    box-sizing:    border-box;
    background:    #f4f4f4;
    border:        1px solid #dedede;
    border-radius: 5px;
    overflow:      hidden;
    position:      relative;
}
.kf-viz--bar-h .kf-viz-bar-fill {
    height:        100%;
    background:    #00374a;
    border-radius: 4px 0 0 4px;
    transition:    width 0.4s ease;
}
.kf-viz--bar-h .kf-viz-bar-count {
    flex:          0 0 auto;
    font-weight:   var(--kf-viz-font-weight, 700);
    min-width:     2.5ch;
    text-align:    left;
}

.kf-viz--bar-v .kf-viz-canvas {
    display:        flex;
    flex-direction: row;
    align-items:    flex-end;
    justify-content:center;
    gap:            8px;
    height:         100%;
    overflow:       hidden;
    padding-bottom: 4px;
}
.kf-viz--bar-v .kf-viz-bar {
    display:        flex;
    flex-direction: column;
    align-items:    center;
    gap:            4px;
    flex:           1 1 0;
    min-width:      0;
    height:         100%;
    color:          #00374a;
    font-size:      13px;
}
.kf-viz--bar-v .kf-viz-bar-track {
    flex:          1 1 auto;
    width:         100%;
    box-sizing:    border-box;
    background:    #f4f4f4;
    border:        1px solid #dedede;
    border-radius: 5px;
    overflow:      hidden;
    display:       flex;
    align-items:   flex-end;
}
.kf-viz--bar-v .kf-viz-bar-fill {
    width:         100%;
    background:    #00374a;
    transition:    height 0.4s ease;
}
.kf-viz--bar-v .kf-viz-bar-count {
    font-weight: var(--kf-viz-font-weight, 700);
}
.kf-viz--bar-v .kf-viz-bar-label {
    font-size:     12px;
    text-align:    center;
    overflow:      hidden;
    text-overflow: ellipsis;
    white-space:   nowrap;
    width:         100%;
}

/* Bare style — strips the card shell for clean page integration. The
 * canvas keeps a single bottom rule so the visualisation still has a
 * subtle baseline against the page. */
.kf-viz--bare {
    border:        none;
    background:    transparent;
    border-radius: 0;
    box-shadow:    none;
    padding:       0;
}
/* Only the bare masonry shows a baseline rule, and only while the canvas
 * is actually cropped (i.e. there are answers below the visible area).
 * The .is-overflowing class is toggled by JS after every render. */
.kf-viz--bare.kf-viz--masonry .kf-viz-canvas.is-overflowing {
    border-bottom: 1px solid #dedede;
}
/* Inset depth shadow lives on the WRAP's ::after so it paints above the
 * items inside the canvas (items establish their own painting order). */
.kf-viz--bare.kf-viz--masonry:has(.kf-viz-canvas.is-overflowing)::after {
    content:        '';
    position:       absolute;
    left:           0;
    right:          0;
    bottom:         0;
    height:         14px;
    box-shadow:     inset 0 -10px 10px -10px rgba(0, 0, 0, 0.18);
    pointer-events: none;
    z-index:        5;
}

/* Empty-state hint shown briefly while the first fetch is in flight. */
.kf-viz-empty {
    display:        flex;
    align-items:    center;
    justify-content:center;
    height:         100%;
    min-height:     100px;
    color:          #aaa;
    font-style:     italic;
    font-size:      14px;
}

/* Masonry row steppers and word-cloud threshold stepper — nav-pair stack.
 * Positioned below the expand button at top-right; hidden by default,
 * fades in on .kf-viz:hover exactly like .kf-viz-expand. */
.kf-viz-stepper-group {
    position:       absolute;
    top:            60px;      /* below expand: 8 + 44 + 8 */
    right:          8px;
    z-index:        3;
    display:        flex;
    flex-direction: column;
    width:          44px;
    border-radius:  10px;
    box-shadow:     0 1px 4px rgba(0, 0, 0, 0.08);
    opacity:        0;
    transition:     opacity 0.15s ease;
}
.kf-viz-stepper-btn {
    width:           44px;
    height:          44px;
    border:          1px solid #dedede;
    border-radius:   0;
    background:      #fff;
    color:           #00374a;
    cursor:          pointer;
    display:         flex;
    align-items:     center;
    justify-content: center;
    font-size:       22px;
    line-height:     1;
    padding:         0;
    transition:      background 0.15s ease,
                     color 0.15s ease,
                     border-color 0.15s ease;
}
/* Joined-pair radii — top button rounds the top, bottom button rounds the bottom */
.kf-viz-stepper-btn:first-child {
    border-top-left-radius:  10px;
    border-top-right-radius: 10px;
    border-bottom:           none;
}
.kf-viz-stepper-btn:last-child {
    border-bottom-left-radius:  10px;
    border-bottom-right-radius: 10px;
}
/* Solo: when the sibling is hidden, the remaining button is a full square */
.kf-viz-stepper-group:has(.is-hidden) .kf-viz-stepper-btn:not(.is-hidden) {
    border-radius: 10px;
    border:        1px solid #dedede;
}
/* Phosphor icon inside the button */
.kf-viz-stepper-btn > .ph {
    display:        block;
    line-height:    1;
    pointer-events: none;
}
.kf-viz-stepper-btn:hover {
    background:   #00a9bb;
    color:        #00374a;
    border-color: #00a9bb;
}
.kf-viz-stepper-btn:active {
    background:   #00262d;
    color:        #fff;
    border-color: #00262d;
}
.kf-viz-stepper-btn.is-hidden { display: none; }

/* ------------------------------------------------------------------
 * Inline visualisation title — rendered server-side at the top of
 * .kf-viz when vizTitle is set; centered, applies to every viz type.
 * ----------------------------------------------------------------- */
.kf-viz-title {
    text-align:    center;
    margin:        0 0 8px;
    color:         #00374a;
    font-size:     16px;
    font-weight:   700;
    line-height:   1.25;
    word-break:    break-word;
    flex:          0 0 auto;
}
.kf-viz--bare > .kf-viz-title { color: inherit; }

/* When a title is present the wrap becomes a flex column so the canvas
 * gets the remaining height instead of overflowing past it. Without
 * this, type-specific canvases that use `height: 100%` (bar-v, masonry,
 * wordcloud) extend beyond the wrap and have their bottom clipped by
 * the wrap's `overflow: hidden` — most visibly the value labels of a
 * vertical bar chart, which sit at the bottom of every bar. */
.kf-viz:has(> .kf-viz-title) {
    display:        flex;
    flex-direction: column;
    align-items:    stretch;
}
.kf-viz:has(> .kf-viz-title) > .kf-viz-canvas {
    flex:        1 1 auto;
    min-height:  0;
    height:      auto;
}

/* ──────────────────────────────────────────────────────────────────────
   AI chat — third qualitative viz mode
   ──────────────────────────────────────────────────────────────────── */

.kf-viz--aichat {
    display:        flex;
    flex-direction: column;
    gap:            12px;
    padding:        16px;
    background:     #fff;
    border:         1px dashed #dedede;
    border-radius:  6px;
}

.kf-viz--aichat .kf-viz-aichat {
    display:        flex;
    flex-direction: column;
    gap:            10px;
    width:          100%;
}

.kf-viz-aichat-form {
    display:    flex;
    gap:        8px;
    align-items: stretch;
}

.kf-viz-aichat-input {
    flex:           1 1 auto;
    min-height:     56px;
    resize:         vertical;
    padding:        10px 12px;
    border:         1px solid #dedede;
    border-radius:  4px;
    font:           inherit;
    line-height:    1.4;
    background:     #fff;
    color:          inherit;
}

.kf-viz-aichat-input:disabled {
    background:     #f7f7f7;
    color:          #999;
    cursor:         not-allowed;
}

.kf-viz-aichat-input::placeholder {
    color:          #dedede;
    opacity:        1;
}

.kf-viz-aichat-send {
    flex:           0 0 auto;
    width:          48px;
    align-self:     stretch;
    border:         1px solid #dedede;
    border-radius:  4px;
    background:     #fff;
    cursor:         pointer;
    font-size:      20px;
    color:          #333;
    transition:     background-color 120ms ease;
}

.kf-viz-aichat-send:hover:not(:disabled)  { background: #f3f3f3; }
.kf-viz-aichat-send:active:not(:disabled) { background: #e8e8e8; }
.kf-viz-aichat-send:disabled              { cursor: not-allowed; color: #ccc; }
.kf-viz-aichat-send > i                   { display: block; line-height: 1; }

.kf-viz-aichat--inactive {
    opacity:    0.5;
    transition: opacity 200ms ease;
}

/* Thinking indicator — three dots that bounce in sequence, plus an
   inline status caption. The caption shows queue position / model name
   / acceleration once the result lands. */
.kf-viz-aichat-thinking {
    display:        flex;
    align-items:    center;
    gap:            10px;
    padding:        8px 4px;
    font-size:      13px;
    color:          #666;
    min-height:     24px;
}

.kf-viz-aichat-thinking[hidden] { display: none; }

.kf-viz-aichat-dot {
    width:          7px;
    height:         7px;
    border-radius:  50%;
    background:     #999;
    display:        inline-block;
    animation:      kf-viz-aichat-bounce 1.2s ease-in-out infinite both;
}
.kf-viz-aichat-dot:nth-of-type(2) { animation-delay: 0.15s; }
.kf-viz-aichat-dot:nth-of-type(3) { animation-delay: 0.30s; }

@keyframes kf-viz-aichat-bounce {
    0%, 80%, 100% { transform: scale(0.6); opacity: 0.4; }
    40%           { transform: scale(1.0); opacity: 1;   }
}

/* View-conversation button — visible once an assistant reply lands.
   Same affordance / corner-iconography family as the masonry / wordcloud
   expand buttons but with a chat-bubble icon so its purpose is clear. */
.kf-viz-aichat-view-results {
    align-self:     flex-start;
    display:        inline-flex;
    align-items:    center;
    gap:            8px;
    padding:        8px 14px;
    border:         1px solid #dedede;
    border-radius:  4px;
    background:     #fff;
    cursor:         pointer;
    font:           inherit;
    color:          #333;
    transition:     background-color 120ms ease;
}

.kf-viz-aichat-view-results[hidden]    { display: none; }
.kf-viz-aichat-view-results:hover      { background: #f3f3f3; }
.kf-viz-aichat-view-results > i        { font-size: 18px; line-height: 1; display: block; }

.kf-viz-aichat-view-results-count {
    display:        inline-flex;
    align-items:    center;
    justify-content: center;
    min-width:      18px;
    height:         18px;
    padding:        0 6px;
    border-radius:  9px;
    background:     #3b82f6;
    color:          #fff;
    font-size:      11px;
    font-weight:    600;
    line-height:    1;
}

.kf-viz-aichat-view-results-count[hidden] { display: none; }

/* Fullscreen conversation modal — reuses the kf-viz-modal base look
   so the close button + backdrop feel identical to the other viz
   modals. Inner content is a vertical chat-bubble list. */
.kf-viz-aichat-modal .kf-viz-aichat-modal-body {
    display:        flex;
    flex-direction: column;
    height:         100%;
    overflow:       auto;
    padding:        clamp(20px, 4vw, 48px);
}

.kf-viz-aichat-msglist {
    display:        flex;
    flex-direction: column;
    gap:            14px;
    max-width:      900px;
    width:          100%;
    margin:         0 auto;
}

.kf-viz-aichat-msg {
    display:        flex;
    width:          100%;
}

.kf-viz-aichat-msg--user      { justify-content: flex-end; }
.kf-viz-aichat-msg--assistant { justify-content: flex-start; }

.kf-viz-aichat-bubble {
    max-width:      80%;
    padding:        12px 16px;
    border-radius:  10px;
    line-height:    1.45;
    white-space:    pre-wrap;
    overflow-wrap:  anywhere;
    background:     #f3f3f3;
    color:          #222;
}

.kf-viz-aichat-msg--user .kf-viz-aichat-bubble {
    background:     #3b82f6;
    color:          #fff;
    border-bottom-right-radius: 2px;
}

.kf-viz-aichat-msg--assistant .kf-viz-aichat-bubble {
    background:     #f3f3f3;
    color:          #222;
    border-bottom-left-radius:  2px;
}

/* Modal-mounted chat blocks inherit the same shell but lose the dashed
   border / padding so they fill the modal cleanly. */
.kf-viz-modal .kf-viz--aichat {
    border:    none;
    background: transparent;
    padding:   0;
}

/* ──────────────────────────────────────────────────────────────────────
   Markdown rendering inside AI chat bubbles.
   Compact spacing so a multi-paragraph reply still fits in a bubble.
   ──────────────────────────────────────────────────────────────────── */

.kf-viz-aichat-bubble p {
    margin: 0 0 0.6em 0;
    line-height: 1.5;
    /* WP / JFB themes often slap a max-width:1000px on every paragraph
       inside the form template. Override here so chat bubbles control
       their own width. */
    max-width: none;
}
.kf-viz-aichat-bubble p:last-child { margin-bottom: 0; }

.kf-viz-aichat-bubble strong { font-weight: 700; }
.kf-viz-aichat-bubble em     { font-style: italic; }

.kf-viz-aichat-bubble h3,
.kf-viz-aichat-bubble h4,
.kf-viz-aichat-bubble h5,
.kf-viz-aichat-bubble h6 {
    margin: 0.8em 0 0.3em 0;
    font-weight: 700;
    line-height: 1.3;
}
.kf-viz-aichat-bubble h3 { font-size: 1.15em; }
.kf-viz-aichat-bubble h4 { font-size: 1.05em; }
.kf-viz-aichat-bubble h5,
.kf-viz-aichat-bubble h6 { font-size: 1em; }
.kf-viz-aichat-bubble h3:first-child,
.kf-viz-aichat-bubble h4:first-child,
.kf-viz-aichat-bubble h5:first-child,
.kf-viz-aichat-bubble h6:first-child { margin-top: 0; }

.kf-viz-aichat-bubble ul,
.kf-viz-aichat-bubble ol {
    margin: 0.3em 0 0.6em 0;
    padding-left: 1.4em;
}
.kf-viz-aichat-bubble ul:last-child,
.kf-viz-aichat-bubble ol:last-child { margin-bottom: 0; }
.kf-viz-aichat-bubble li {
    margin: 0.15em 0;
    line-height: 1.45;
}

.kf-viz-aichat-bubble code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.92em;
    background: rgba(0, 0, 0, 0.06);
    padding: 1px 5px;
    border-radius: 3px;
}
.kf-viz-aichat-msg--user .kf-viz-aichat-bubble code {
    background: rgba(255, 255, 255, 0.18);
}

.kf-viz-aichat-bubble pre {
    margin: 0.5em 0;
    padding: 10px 12px;
    background: rgba(0, 0, 0, 0.06);
    border-radius: 5px;
    overflow-x: auto;
    font-size: 0.92em;
}
.kf-viz-aichat-bubble pre code {
    background: transparent;
    padding: 0;
    border-radius: 0;
}

/* Modal-mounted chat container — explicitly inherits the host viz wrap's
   font-family (set inline by openConversationModal). The body's default
   Cocracy must not bleed in. */
.kf-viz-aichat-modal {
    font-family: inherit;
}
.kf-viz-aichat-modal-body,
.kf-viz-aichat-msglist,
.kf-viz-aichat-bubble {
    font-family: inherit;
}

/* ──────────────────────────────────────────────────────────────────────
   AI chat refinements (override block) — appended last so these rules
   win cascade order against the original `.kf-viz-aichat*` declarations.
   ──────────────────────────────────────────────────────────────────── */

/* Bare style: drop the dashed frame entirely so it matches the
   wordcloud / masonry bare presentation (just the content, no chrome). */
.kf-viz--aichat.kf-viz--bare {
    border:     0;
    background: transparent;
    padding:    0;
}

/* Default (non-bare) aichat block: keep a soft visual container but
   slightly tighter so it sits naturally next to other viz blocks. */
.kf-viz--aichat {
    padding:       14px;
    border:        1px solid rgba(0, 0, 0, 0.08);
    border-radius: 6px;
    background:    #fff;
}

/* Form container becomes the positioning context for the inset send
   button — and the input no longer needs to share width with a side
   button column. */
.kf-viz-aichat-form {
    position:    relative;
    display:     block;
    gap:         0;
}

.kf-viz-aichat-input {
    width:          100%;
    box-sizing:     border-box;
    /* Leave room for the inset send button bottom-right. */
    padding-right:  52px;
    padding-bottom: 12px;
}

/* Send button — repositioned inside the bottom-right corner of the
   textarea, sized similarly to JFB form-field affordances. */
.kf-viz-aichat-send {
    position:       absolute;
    right:          8px;
    bottom:         8px;
    width:          36px;
    height:         36px;
    align-self:     unset;
    border-radius:  4px;
    border:         1px solid rgba(0, 0, 0, 0.12);
    background:     #fff;
    font-size:      18px;
    display:        flex;
    align-items:    center;
    justify-content:center;
    line-height:    1;
    color:          #333;
}
.kf-viz-aichat-send:hover:not(:disabled)  { background: #f3f3f3; }
.kf-viz-aichat-send:active:not(:disabled) { background: #e8e8e8; }

/* View-conversation button — promoted into the same affordance family
   as the other viz modes' top-right expand button. The template adds
   `.kf-viz-expand` so we inherit position / shape / hover behaviour;
   only the hidden state and the per-block opacity gate need overriding. */
.kf-viz-aichat-view-results.kf-viz-expand {
    opacity: 1;       /* always-visible when there's a conversation */
}
.kf-viz-aichat-view-results.kf-viz-expand[hidden] { display: none; }

/* Tiny count badge over the corner of the expand button. */
.kf-viz-aichat-view-results-count {
    position:       absolute;
    top:            -4px;
    right:          -4px;
    min-width:      18px;
    height:         18px;
    padding:        0 5px;
    border-radius:  9px;
    background:     #3b82f6;
    color:          #fff;
    font-size:      11px;
    font-weight:    600;
    line-height:    18px;
    text-align:     center;
    pointer-events: none;
    box-shadow:     0 0 0 2px #fff;
}
.kf-viz-aichat-view-results-count[hidden] { display: none; }

/* Make sure the AI chat block is the positioning context for the
   absolutely-positioned expand button. The base `.kf-viz` already
   relies on this for the existing expand-button; keep it explicit. */
.kf-viz--aichat { position: relative; }

/* Bubbles + their markdown children must follow the host viz block's
   font choice. Theme stylesheets routinely set font-family on <p>, <li>,
   <h3>–<h6>, which would otherwise override inherit. */
.kf-viz-aichat-modal,
.kf-viz-aichat-modal-body,
.kf-viz-aichat-msglist,
.kf-viz-aichat-msg,
.kf-viz-aichat-bubble,
.kf-viz-aichat-bubble p,
.kf-viz-aichat-bubble li,
.kf-viz-aichat-bubble h1,
.kf-viz-aichat-bubble h2,
.kf-viz-aichat-bubble h3,
.kf-viz-aichat-bubble h4,
.kf-viz-aichat-bubble h5,
.kf-viz-aichat-bubble h6,
.kf-viz-aichat-bubble strong,
.kf-viz-aichat-bubble em,
.kf-viz-aichat-bubble blockquote {
    font-family: inherit;
}

/* Code-style elements get their own monospace stack regardless of the
   inherited body font. */
.kf-viz-aichat-bubble code,
.kf-viz-aichat-bubble pre,
.kf-viz-aichat-bubble pre code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* ──────────────────────────────────────────────────────────────────────
   AI chat refinements v2 — appended after all earlier kf-viz-aichat
   rules so cascade order favours these declarations. Replaces the
   intermediate v1 overrides with a coherent set:

   - View-conversation button: visual language of `#krskrt-mm-panel > button`
     (44×44, white bg, #dedede border, mint hover).
   - Send button (inline + modal): visual language of `.mm-cs-stepper-btn`
     (transparent, no border, mint hover, #00374a icon).
   - Inline aichat: respects the block's max-height — textarea fills the
     space, dots + view button sit in a compact footer row.
   - Modal: header / scrollable messages / footer input — full viewport,
     follow-up question can be asked without leaving fullscreen.
   ──────────────────────────────────────────────────────────────────── */

/* Bare style: zero chrome. Matches the bare presentation of word cloud /
   masonry — author embeds it inside a designed layout and the block
   should not impose its own frame. */
.kf-viz--aichat.kf-viz--bare {
    border:     0;
    background: transparent;
    padding:    0;
}

/* The outer aichat wrap is layout-only: flex column + positioning context
   for the absolutely-positioned view-conversation button. The visible
   frame (border, radius, background) lives on the textarea itself, so we
   never get a doubled box (outer wrap + inner input). The bare-style
   modifier flips ONLY the textarea's frame off — see further down. */
.kf-viz--aichat {
    position:       relative;
    padding:        0;
    border:         0;
    border-radius:  0;
    background:     transparent;
    box-sizing:     border-box;
    /* Inline height comes from the wrap's inline `style="height: …"` — see
       templates/blocks/data-viz.php which now applies max-height for
       aichat just like it does for wordcloud / bar-v. */
    display:        flex;
    flex-direction: column;
}

/* Bare modifier: the outer wrap is already chrome-less for aichat (see
   above — padding/border/background are all zeroed regardless of bare).
   The textarea is a functional input field and KEEPS its frame in both
   bare and non-bare modes — a chat box without a frame just reads as
   loose text. So `.kf-viz--bare` is effectively a no-op for the
   aichat viz type; left in place for parity with wordcloud / masonry. */

/* Inner shell — fills the wrap; the textarea grows to take whatever
   space remains after the bottom status row. */
.kf-viz--aichat > .kf-viz-aichat {
    flex:           1 1 auto;
    display:        flex;
    flex-direction: column;
    gap:            10px;
    min-height:     0;
}

.kf-viz-aichat-form {
    position:       relative;
    flex:           1 1 auto;
    display:        flex;
    min-height:     0;
}

.kf-viz-aichat-input {
    flex:           1 1 auto;
    width:          100%;
    box-sizing:     border-box;
    /* Leave room for the inset send button bottom-right. */
    padding:        10px 52px 12px 12px;
    border:         1px solid #dedede;
    border-radius:  4px;
    font:           inherit;
    font-family:    inherit;
    line-height:    1.4;
    background:     #fff;
    color:          inherit;
    resize:         none;
    min-height:     56px;
}

.kf-viz-aichat-input:disabled {
    background:     #f7f7f7;
    color:          #999;
    cursor:         not-allowed;
}

.kf-viz-aichat-input::placeholder {
    color:          #dedede;
    opacity:        1;
}

/* Send button — `.mm-cs-stepper-btn` visual language: transparent,
   borderless, mint hover, #00374a icon colour. Sits absolutely in the
   bottom-right corner of the textarea. */
.kf-viz-aichat-send {
    position:       absolute;
    right:          6px;
    bottom:         6px;
    width:          28px;
    height:         28px;
    padding:        0;
    border:         0;
    background:     transparent;
    color:          #00374a;
    cursor:         pointer;
    display:        flex;
    align-items:    center;
    justify-content:center;
    line-height:    1;
    font-size:      18px;
    border-radius:  4px;
    transition:     background 0.1s ease, color 0.1s ease;
}
.kf-viz-aichat-send:hover:not(:disabled)  { background: rgba(0, 55, 74, 0.08); }
.kf-viz-aichat-send:active:not(:disabled) { background: rgba(0, 55, 74, 0.15); }
.kf-viz-aichat-send:disabled              { color: #b3b3b3; cursor: not-allowed; }
.kf-viz-aichat-send > i                   { display: block; line-height: 1; pointer-events: none; }

.kf-viz-aichat--inactive {
    opacity:    0.5;
    transition: opacity 200ms ease;
}

/* Thinking indicator — three dots that bounce in sequence, plus an
   inline status caption. */
.kf-viz-aichat-thinking {
    display:        flex;
    align-items:    center;
    gap:            10px;
    padding:        4px 2px;
    font-size:      13px;
    color:          #666;
    min-height:     24px;
}
.kf-viz-aichat-thinking[hidden] { display: none; }

.kf-viz-aichat-dot {
    width:          7px;
    height:         7px;
    border-radius:  50%;
    background:     #00374a;
    display:        inline-block;
    animation:      kf-viz-aichat-bounce 1.2s ease-in-out infinite both;
}
.kf-viz-aichat-dot:nth-of-type(2) { animation-delay: 0.15s; }
.kf-viz-aichat-dot:nth-of-type(3) { animation-delay: 0.30s; }

@keyframes kf-viz-aichat-bounce {
    0%, 80%, 100% { transform: scale(0.6); opacity: 0.4; }
    40%           { transform: scale(1.0); opacity: 1;   }
}

/* ──────────────────────────────────────────────────────────────────────
   View-conversation button.

   The template adds the class `.kf-viz-expand` so the base positioning
   (top-right of the viz block) is inherited from existing wordcloud /
   masonry expand buttons. The visual language though is `krskrt-fs-btn`
   (44×44 white with #dedede border + mint hover + box-shadow), not the
   round dark-translucent overlay of `.kf-viz-expand`.

   We override the inherited `.kf-viz-expand` declarations for this
   specific button only, leaving the wordcloud/masonry buttons alone.
   ──────────────────────────────────────────────────────────────────── */
.kf-viz-aichat-view-results.kf-viz-expand {
    position:        absolute;
    top:             8px;
    right:           8px;
    width:           44px;
    height:          44px;
    border-radius:   10px;
    background:      #fff;
    color:           #00374a;
    border:          1px solid #dedede;
    padding:         0;
    cursor:          pointer;
    display:         flex;
    align-items:     center;
    justify-content: center;
    font-size:       24px;
    line-height:     1em;
    opacity:         1;       /* always visible (vs. wordcloud's hover-only) */
    box-shadow:      0 1px 4px rgba(0, 0, 0, 0.08);
    transition:      background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
    z-index:         3;
}
.kf-viz-aichat-view-results.kf-viz-expand[hidden] { display: none; }
.kf-viz-aichat-view-results.kf-viz-expand:hover {
    background:   #00a9bb;
    color:        #00374a;
}
.kf-viz-aichat-view-results.kf-viz-expand:active {
    background:   #00262d;
    color:        #fff;
    border-color: #00262d;
}
.kf-viz-aichat-view-results.kf-viz-expand > i {
    display:     block;
    line-height: 1;
    font-size:   24px;
}

/* Count badge — perfectly circular dot in the top-left corner of the
   view-conversation button. Mirrors `.krskrt-mm-dot` from the magic-
   marker plugin (connection-count badge on the Magic Marker button) so
   both indicators share one visual language across the central screen. */
.kf-viz-aichat-view-results-count {
    position:        absolute;
    top:             -4px;
    left:            -4px;
    z-index:         1;
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    width:           20px;
    height:          20px;
    padding:         0;
    border-radius:   50%;
    background:      #22c55e;
    color:           #fff;
    font-size:       11px;
    font-weight:     700;
    line-height:     1;
    flex-shrink:     0;
    pointer-events:  none;
    transition:      background 0.3s;
}
.kf-viz-aichat-view-results-count[hidden] { display: none; }

/* ──────────────────────────────────────────────────────────────────────
   Conversation modal layout:

       ┌─────────────────────────────[close]─┐
       │                                      │
       │       scrollable message list        │
       │                                      │
       │                                      │
       ├──────────────────────────────────────┤
       │  [textarea         ] [send-icon ▶]   │   ← follow-up footer
       └──────────────────────────────────────┘
   ──────────────────────────────────────────────────────────────────── */
.kf-viz-aichat-modal {
    /* Inherits .kf-viz-modal (fixed inset:0). Override the default
       row direction here — we want close pinned + body grows + footer
       pinned. */
    display:         flex;
    flex-direction:  column;
    padding:         0;
}
.kf-viz-aichat-modal .kf-viz-aichat-modal-body {
    flex:            1 1 auto;
    min-height:      0;
    overflow:        auto;
    padding:         clamp(24px, 5vw, 64px) clamp(20px, 4vw, 48px);
    display:         flex;
    flex-direction:  column;
}
.kf-viz-aichat-msglist {
    display:        flex;
    flex-direction: column;
    gap:            14px;
    max-width:      900px;
    width:          100%;
    margin:         0 auto;
    flex:           1 1 auto;
}
.kf-viz-aichat-msg {
    display:        flex;
    width:          100%;
}
.kf-viz-aichat-msg--user      { justify-content: flex-end;   }
.kf-viz-aichat-msg--assistant { justify-content: flex-start; }
.kf-viz-aichat-msg.is-pending {
    opacity:    0.5;
    font-style: italic;
}
.kf-viz-aichat-msg.is-error .kf-viz-aichat-bubble {
    background: #fff3cd;
    color:      #7a4a00;
}

.kf-viz-aichat-bubble {
    max-width:      80%;
    padding:        12px 16px;
    border-radius:  10px;
    line-height:    1.45;
    white-space:    pre-wrap;
    overflow-wrap:  anywhere;
    background:     #f3f3f3;
    color:          #222;
}
.kf-viz-aichat-msg--user .kf-viz-aichat-bubble {
    background:     #00374a;
    color:          #fff;
    border-bottom-right-radius: 2px;
}
.kf-viz-aichat-msg--assistant .kf-viz-aichat-bubble {
    background:     #f3f3f3;
    color:          #222;
    border-bottom-left-radius:  2px;
}

/* Modal footer — full-width strip pinned to the bottom of the modal,
   holding the follow-up form and a thinking-row that takes the place
   of the inline thinking row when the modal is open. */
.kf-viz-aichat-modal-footer {
    flex:            0 0 auto;
    border-top:      1px solid #ecedef;
    background:      #fff;
    padding:         14px clamp(20px, 4vw, 48px) 18px;
    display:         flex;
    flex-direction:  column;
    gap:             8px;
}
.kf-viz-aichat-modal-footer .kf-viz-aichat-form {
    max-width:      900px;
    width:          100%;
    margin:         0 auto;
    min-height:     0;
    flex:           0 0 auto;
}
.kf-viz-aichat-modal-footer .kf-viz-aichat-input {
    min-height:     64px;
}

/* Container chain — let normal inheritance carry the modal's inline
   `style="font-family: <hostFont>"` down to the bubble. We must NOT use
   !important on these container selectors, or we'd outrank the inline
   style itself (specificity-of-inline-style beats CSS selectors EXCEPT
   when the CSS rule is !important — which would then force the modal
   to fall back to `inherit` from <body>, which is Cocracy, and the
   whole point breaks). */
.kf-viz-aichat-modal,
.kf-viz-aichat-modal-body,
.kf-viz-aichat-msglist,
.kf-viz-aichat-msg,
.kf-viz-aichat-bubble {
    font-family: inherit;
}

/* Markdown tags inside a bubble — the magic-marker plugin injects an
   inline `<style id="krskrt-mm-font-override">` that pins
   `font-family: Cocracy !important` on p / ul / ol / li / dl / dt / dd
   / h1-6. We MUST match that with !important here, otherwise the
   inherited Lekton from the bubble is overruled the moment we hit one
   of these tags. (Specificity (0,1,1) >= (0,0,1) — both !important —
   ours wins as the later, higher-specificity rule.) */
.kf-viz-aichat-bubble p,
.kf-viz-aichat-bubble ul,
.kf-viz-aichat-bubble ol,
.kf-viz-aichat-bubble li,
.kf-viz-aichat-bubble dl,
.kf-viz-aichat-bubble dt,
.kf-viz-aichat-bubble dd,
.kf-viz-aichat-bubble h1,
.kf-viz-aichat-bubble h2,
.kf-viz-aichat-bubble h3,
.kf-viz-aichat-bubble h4,
.kf-viz-aichat-bubble h5,
.kf-viz-aichat-bubble h6,
.kf-viz-aichat-bubble blockquote,
.kf-viz-aichat-bubble strong,
.kf-viz-aichat-bubble em,
.kf-viz-aichat-bubble table,
.kf-viz-aichat-bubble td,
.kf-viz-aichat-bubble th,
.kf-viz-aichat-bubble a {
    font-family: inherit !important;
}
.kf-viz-aichat-bubble code,
.kf-viz-aichat-bubble pre,
.kf-viz-aichat-bubble pre code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace !important;
}

/* Clean focus state for the chat textarea — the browser default outline
   renders as a dotted/black box on a borderless textarea, which looked
   like a stray dark rectangle behind the field. Replace with a soft
   ring in the krskrt palette so focus is still obvious. */
.kf-viz-aichat-input {
    outline: none;
}
.kf-viz-aichat-input:focus,
.kf-viz-aichat-input:focus-visible {
    outline:    none;
    border-color: #00a9bb;
    box-shadow: 0 0 0 2px rgba(0, 169, 187, 0.25);
}

/* ──────────────────────────────────────────────────────────────────────
   AI word cloud — refresh button + spinning state.

   Shape matches the krskrt-fs-btn / view-conversation-button family:
   44×44 white square with #dedede border, 10px radius, mint hover.
   Lives top-right of the viz wrap; pulses with a spinning icon while
   inference is running.

   The wordcloud itself uses the existing `.kf-viz-canvas` + `.kf-viz-tag`
   styles, so no extra rules for the body of the cloud.
   ──────────────────────────────────────────────────────────────────── */

.kf-viz--aicloud {
    position: relative;
}

/* Refresh-button per-state styling (chrome lives in the shared rule
 * near the top of this file). data-state is set by the controller in
 * krskrt-data-viz-aicloud.js: idle / thinking / error. */
.kf-viz-aicloud-refresh:disabled        { cursor: not-allowed; }
.kf-viz-aicloud-refresh--inactive       { opacity: 0.5; }
.kf-viz-aicloud-refresh > i             { transition: transform 0.2s ease; }
.kf-viz-aicloud-refresh[data-state="thinking"] {
    color:        #00a9bb;
    border-color: #00a9bb;
}
.kf-viz-aicloud-refresh[data-state="thinking"] > i {
    animation: kf-aicloud-spin 1.1s linear infinite;
}
.kf-viz-aicloud-refresh[data-state="error"] {
    color:        #b54708;
    border-color: #fcd34d;
}

@keyframes kf-aicloud-spin {
    to { transform: rotate(360deg); }
}

/* ── AI cloud mindmap focus mode ──────────────────────────────────────
 * Triggered by clicking a phrase WHEN the cloud is rendered inside the
 * fullscreen .kf-viz-modal (the inline cloud stays purely visual until
 * the user opens fullscreen via the corners-out button).
 *
 * On click: the clicked .kf-viz-tag stays in the DOM, gets `.is-focus`
 * and is absolutely-positioned at canvas centre. All other tags get
 * `.is-blurred` and fade/blur out. Variant pills + plain-text answers
 * are appended around the centre. Layout positions are computed in JS
 * (see enterFocus/layout in krskrt-data-viz-aicloud.js).
 *
 * No new modal. No connector lines. The variants' pill styling marks
 * the boundary between centre phrase and the outer answer ring; the
 * answers themselves are pure centered text — no border, no background.
 */
/* The modal aicloud canvas needs to be a positioning context so the
 * popup (position:absolute; inset:0) can mount inside it. Set this
 * always — not only when the .is-focusing class is on — so there's
 * no first-paint glitch on focus enter. Inline (non-modal) clouds
 * stay default (no popup mounts there). */
.kf-viz-modal .kf-viz--aicloud .kf-viz-canvas {
    position: relative;
}
/* Only the in-modal cloud is clickable. The inline cloud stays calm. */
.kf-viz-modal .kf-viz--aicloud .kf-viz-tag {
    cursor: pointer;
}
.kf-viz-modal .kf-viz--aicloud .kf-viz-tag:hover,
.kf-viz-modal .kf-viz--aicloud .kf-viz-tag:focus-visible {
    outline:   none;
    transform: scale(1.06);
    transition: transform 140ms ease;
}

/* ── Mindmap popup ───────────────────────────────────────────────────
 * White overlay that covers the word-cloud area when a phrase is
 * clicked. Pure flow layout: phrase → variants → answers masonry.
 * Scrolls vertically when the answers don't fit. Vertically centred
 * when they do.
 */
.kf-viz-mindmap-popup {
    /* Popup itself is a flex COLUMN with NO overflow — the inner is
     * the scroll container. Decoupling lets the inner flex-centre its
     * content vertically when it fits AND scroll when it doesn't,
     * without the centering-collapse-on-overflow browser quirk. */
    position:        absolute;
    inset:           0;
    z-index:         10;
    background:      rgba(255, 255, 255, 0.97);
    overflow:        hidden;
    display:         flex;
    flex-direction:  column;
    padding:         0 24px;
    box-sizing:      border-box;
    animation:       kfViewMindmapFade 180ms ease-out;
}
@keyframes kfViewMindmapFade {
    from { opacity: 0; }
    to   { opacity: 1; }
}
/* Top bar: prev / close / next, mirroring the masonry item-zoom bar. */
.kf-viz-mindmap-popup-topbar {
    position:        absolute;
    top:             12px;
    right:           12px;
    display:         flex;
    gap:             6px;
    z-index:         11;
}
.kf-viz-mindmap-popup-prev,
.kf-viz-mindmap-popup-next,
.kf-viz-mindmap-popup-close {
    width:           40px;
    height:          40px;
    border-radius:   50%;
    background:      rgba(0, 55, 74, 0.10);
    color:           #00374a;
    border:          0;
    cursor:          pointer;
    font-size:       20px;
    display:         flex;
    align-items:     center;
    justify-content: center;
    transition:      background 0.15s ease;
    padding:         0;
}
.kf-viz-mindmap-popup-prev:hover,
.kf-viz-mindmap-popup-next:hover,
.kf-viz-mindmap-popup-close:hover { background: rgba(0, 55, 74, 0.22); }
.kf-viz-mindmap-popup-inner {
    /* The scrollable surface. `safe center` is the crucial keyword:
     * when content fits, it centres vertically; when content is
     * taller than the inner, it falls back to flex-start so the
     * TOP of the content stays scrollable (without `safe`, the
     * browser pushes the content above the visible area and the
     * user cannot scroll back to see the phrase). */
    flex:            1 1 auto;
    min-height:      0;
    overflow-y:      auto;
    overflow-x:      hidden;
    display:         flex;
    flex-direction:  column;
    align-items:     center;
    justify-content: safe center;
    max-width:       1100px;
    margin:          0 auto;
    width:           100%;
    text-align:      center;
    /* Some breathing room top/bottom so the first/last item never
     * sits flush against the scroll boundary. */
    padding:         8px 0 16px;
    box-sizing:      border-box;
    /* Cross-fade on prev/next phrase swap. JS toggles opacity to 0
     * just before rebuilding content, then back to 1 on next frame.
     * The transition smooths the swap so the underlying cloud doesn't
     * flash through. */
    transition:      opacity 140ms ease;
}
.kf-viz-mindmap-popup-inner > * {
    flex-shrink:     0;
    max-width:       100%;
    width:           100%;
}
.kf-viz-mindmap-popup-phrase {
    font-size:       clamp(28px, 4vw, 52px);
    font-weight:     700;
    color:           #00374a;
    line-height:     1.15;
    margin:          0 0 18px;
    word-break:      break-word;
    /* Leave clearance for the absolutely-positioned topbar (prev/x/next
     * = ~150 px wide top-right). The padding only takes effect when
     * the phrase line would otherwise reach into that corner. */
    padding:         0 8px;
}
.kf-viz-mindmap-popup-variants {
    display:         flex;
    flex-wrap:       wrap;
    gap:             8px 10px;
    justify-content: center;
    margin:          0 auto 28px;
    max-width:       900px;
}
.kf-viz-mindmap-popup-variant {
    display:         inline-block;
    padding:         6px 14px;
    border-radius:   999px;
    background:      #e6f4f8;
    color:           #155e75;
    font-size:       14px;
    font-weight:     600;
    line-height:     1.25;
    white-space:     nowrap;
}
/* True masonry: fixed-width columns laid out via CSS grid; each
 * column is a flex-column flow where items stack with their natural
 * height. JS (distributeAnswersMasonry) packs items into the
 * currently-shortest column. Mirrors the original .kf-viz--masonry
 * pattern so the AI popup feels native. */
.kf-viz-mindmap-popup-answers {
    display:               grid;
    grid-template-columns: repeat(var(--kf-aicloud-cols, 3), minmax(0, 1fr));
    gap:                   0 12px;
    width:                 100%;
    align-items:           start;
}
.kf-viz-mindmap-popup-col {
    display:         flex;
    flex-direction:  column;
    min-width:       0;
}
.kf-viz-mindmap-popup-answer {
    position:        relative;
    background:      #fff;
    border:          1px solid #dedede;
    border-radius:   5px;
    padding:         10px 12px;
    margin:          0 0 10px;
    color:           #00374a;
    font-size:       13px;
    line-height:     1.35;
    text-align:      left;
    word-break:      break-word;
    transition:      box-shadow 0.2s ease, transform 0.15s ease;
}
.kf-viz-mindmap-popup-answer:hover {
    transform:       translateY(-1px);
    box-shadow:      0 4px 14px rgba(0, 55, 74, 0.12);
}

/* Per-answer hide 'x' on AI word cloud quote cards (Central only). Mirrors
 * .kf-viz-item-hide; distinct class so the global hide delegation doesn't
 * double-handle it (the AI cloud wires its own click → kfHideAnswers). */
.kf-viz-answer-hide {
    position:        absolute;
    top:             4px;
    right:           4px;
    width:           22px;
    height:          22px;
    padding:         0;
    display:         flex;
    align-items:     center;
    justify-content: center;
    background:      #fff;
    color:           #00374a;
    border:          1px solid #dedede;
    border-radius:   6px;
    cursor:          pointer;
    font-size:       13px;
    line-height:     1;
    box-shadow:      0 1px 3px rgba(0, 0, 0, 0.12);
    opacity:         0;
    transition:      opacity 0.12s ease, background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
    z-index:         2;
}
.kf-viz-mindmap-popup-answer:hover .kf-viz-answer-hide,
.kf-viz-answer-hide:focus-visible {
    opacity: 1;
}
.kf-viz-answer-hide:hover {
    background:   #00a9bb;
    color:        #00374a;
    border-color: #00a9bb;
}
.kf-viz-answer-hide > i { display: block; line-height: 1; pointer-events: none; }
