/* NJ-99993: AI Correction Review (EN platform) — lesson-note transcription panel.
 *
 * SEPARATE FILE, not appended to details_textbook.css, for the same reason ai_review.js is separate
 * from transcript.js: the transcription panel is shipped, high-traffic behaviour and BUSINESS_IMPACT
 * rates edits to it MEDIUM risk. A stray selector or an unclosed brace in a shared stylesheet can
 * restyle the existing panel; in its own file the worst case is that AI Review looks wrong.
 *
 * Every selector is prefixed .ai-review / .ai-review-drawer / .ai-review-fab so nothing can collide
 * with the existing .transcription-* rules.
 *
 * Follows Yamazaki's design (DEP-1). Word tiles are sized for SHORT PHRASES, not single words —
 * FR-042 requires "come up with an idea" to fit alongside its meaning and example without truncating,
 * because phrases are the preferred content, not the exception.
 */

/* ── Positioning context ─────────────────────────────────────────────────────── */

/* ── Container: a plain mount point. BOTH children are position: fixed. ─────────
 *
 * Third and final approach to this positioning problem; the two rejected ones are in ADV-013 and the
 * third is here, because each failed for the same reason — assuming something about the scroll structure.
 *
 *   1. `position: absolute; inset: 0` inside #transcription-container. An absolutely positioned child of
 *      a scroll container resolves inset against the scroller's PADDING BOX — the whole scrollable
 *      height — so `bottom: 16px` landed 16px above the end of all 63 pages.
 *   2. Relocating the overlay to the scroll container's parent via JS. The pill then scrolled with a
 *      different ancestor, because the guess about which element scrolls was wrong.
 *   3. `position: sticky` on this container. Correct while scrolling, but sticky UN-STICKS once the
 *      element reaches the end of its containing block — so at maximum scroll the pill drifted up with
 *      the last transcript entries. That is sticky's defined behaviour, not a tuning problem, so no
 *      sticky configuration could fix it (ADV-024).
 *
 * The pill and the drawer are now both `position: fixed`, with coordinates written by
 * ai_review.js from #transcription-container / .area-chat bounding rects and refreshed on scroll and
 * resize. Fixed positioning is scroll-invariant by definition, which is the property actually needed.
 *
 * The container itself stays a DOM child of #transcription-container ON PURPOSE: the transcript tab is
 * shown/hidden by toggling `hide` on that element, and `display: none` on an ancestor still hides a
 * fixed descendant. That is what keeps the pill from floating over the メッセージ / チャット / メモ tabs
 * without any JS observing class changes.
 *
 * No positioning context is added to any existing element, so the T071 risk about re-anchoring the
 * per-entry "..." menu popup and the copy toast does not apply.
 */

#ai-review-container {
    /* z-index makes this a stacking context, and because both children are fixed, THIS value — not
     * theirs — decides whether the drawer paints over the 文字起こし / メッセージ / チャット / メモ tab row.
     * Deliberately not enormous: the drawer is confined to the panel's rect, so it should not out-stack
     * a site-wide modal or the global header. */
    z-index: 100;
    position: relative;
}

/* ── Trigger pill ────────────────────────────────────────────────────────────── */

/* Hidden while the drawer is open — the drawer covers the panel, so a pill on top of it is noise.
 * This also gives `.is-open` a purpose; it was previously set by the JS and referenced nowhere
 * (ADV-021). */
#ai-review-container.is-open .ai-review-fab {
    display: none;
}

.ai-review-fab {
    /* fixed, with right/bottom written by _positionFab() from the panel rect. The values below only
     * apply before the first measurement.
     *
     * ADV-032: HIDDEN until placed. The transcript tab starts hidden, so the first measurement is
     * correctly skipped (zero rect, ADV-028) — which meant the pill was briefly painted at these
     * fallback offsets, i.e. the far bottom-right of the WINDOW rather than of the panel, until a scroll
     * triggered a real measurement. `visibility` rather than `display` so layout is unaffected. */
    position: fixed;
    right: 16px;
    bottom: 16px;
    visibility: hidden;

    /* ── Values below are the design's, verbatim: UI-Design/css/review.css .btn_ai_review +
     * tokens.css (Figma 2941:4084). Do not re-tune them by eye.
     *
     * What was here before was a Tailwind-ish orange (#f97316 → #fb923c at 135deg) with a soft
     * box-shadow — close enough to pass a glance, wrong on every value. --ai-gradient is a bespoke
     * Figma stop pair (RedOrange/500 → Orange/400 at 157.38deg) that the design deliberately notes is
     * NOT V2's brand-primary-gradient. Sizing is height-driven (40px) rather than padding-driven, so
     * the pill keeps its height regardless of font metrics or locale. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    height: 40px;
    padding: 0 16px;
    border: none;
    border-radius: 9999px;
    background: linear-gradient(157.38deg, #ff6b35 0%, #f4a343 100%);
    color: #fff;
    font-size: 14px;
    font-weight: 700;
    line-height: 1;
    letter-spacing: .07px;
    white-space: nowrap;
    cursor: pointer;
    filter: drop-shadow(0 3px 5px rgba(241, 137, 14, .4));
    isolation: isolate;
    transition: all .2s linear;
}

/* Hover/active are an OVERLAY, not an opacity or shadow change (V2 §3.1.8). The design is explicit
 * about why: opacity would let the textbook page behind show through the pill. */
.ai-review-fab::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: transparent;
    transition: background .2s linear;
    pointer-events: none;
}

.ai-review-fab:hover::after {
    background: rgba(0, 0, 0, .04);
}

.ai-review-fab:active::after {
    background: rgba(0, 0, 0, .12);
}

/* Set by _positionFab() once real coordinates have been written. */
.ai-review-fab.is-placed {
    visibility: visible;
}

/* The sparkle must never be squeezed by the label. Without `flex: none` a long localised button string
 * (pt-br's is the longest) shrinks the icon instead of widening the pill, so the star ends up a
 * different size per locale — the same "star doesn't match" symptom, arriving by a different route. */
.ai-review-fab .ai-review__icon,
.ai-review-drawer__title .ai-review__icon {
    flex: none;
}

/* Hover and active are handled by the ::after overlay above. A shadow-growth hover and a translateY
 * active state used to live here; both are off-spec (V2 §3.1.8 is an overlay system) and the transform
 * additionally shifted the drop-shadow, which read as the pill jumping. */
.ai-review-fab:focus-visible {
    outline: 2px solid #9d5909;   /* Orange/700 — legible against the gradient */
    outline-offset: 2px;
}

/* No pulsing animation. JA animates its button to advertise the 48-hour window; EN has no window
 * (FR-004 / J-4), so there is nothing to be urgent about — and an infinite animation would need
 * prefers-reduced-motion handling for no benefit. */

/* ── Room for the pill at the end of the transcript ──────────────────────────────
 *
 * The pill is `position: fixed` over the panel's bottom-right, so at MAXIMUM SCROLL it sits permanently
 * on top of the last transcript entry — the member can never read that line. The design solves it with
 * `#view-transcript{padding-bottom:72px}` ("FAB がスクロール末尾を覆い続けないよう、最後まで送れる余白を
 * 確保する").
 *
 * 72px = the pill's 40px height + its 16px offset + 16px clearance.
 *
 * Class-scoped, added by _positionFab(), so the shipped panel is unaffected on lessons with no AI review.
 * The E2E geometry tests did NOT catch this: they assert the pill's box is inside the panel's box, which
 * stayed true the whole time it was covering content. */
#transcription-container.has-ai-review-pill {
    padding-bottom: 72px;
}

/* ── Drawer ──────────────────────────────────────────────────────────────────── */

/* FIXED, with top/left/width/height written by ai_review.js::_positionDrawer() from the panel's
 * bounding rect, and refreshed on scroll and resize.
 *
 * Fixed rather than absolute for the same reason the container is sticky: absolute inside a scroll
 * container resolves against the scrolled content, and this drawer has to cover the VISIBLE panel.
 * Reading the rect makes no assumption about which ancestor scrolls — the assumption is what went
 * wrong twice (ADV-013). */
.ai-review-drawer {
    position: fixed;
    display: flex;
    flex-direction: column;
    background: #fff;
    box-shadow: 0 0 0 1px rgba(15, 23, 42, .08);
    /* Explicit, so the drawer cannot inherit an alignment from any ancestor. All eight supported
     * locales are LTR, so `left` is correct rather than a logical property. */
    text-align: left;
}

.ai-review-drawer[hidden] {
    display: none;
}

.ai-review-drawer__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex: 0 0 auto;
    padding: 14px 16px;
    /* Same --ai-gradient as the trigger. V2 assigns gradients to headers and solid brand-primary to
     * buttons; the design deliberately shares this one bespoke gradient across both (Figma 2941:4084). */
    background: linear-gradient(157.38deg, #ff6b35 0%, #f4a343 100%);
    color: #fff;
}

.ai-review-drawer__title {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 18px;
    line-height: 1.5;
    font-weight: 700;
    white-space: nowrap;
}

/* 20px glyph in a 32px circular hit area, per the design. `transition: none` is intentional — the
 * design specifies the close state changes instantly. */
.ai-review-drawer__close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border: none;
    border-radius: 9999px;
    background: transparent;
    color: #fff;
    cursor: pointer;
    transition: none;
}

/* V2 §3.1.8 overlay-selected-on-dark (white 20%). */
.ai-review-drawer__close:hover {
    background: rgba(255, 255, 255, .2);
}

.ai-review-drawer__close:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 1px;
}

.ai-review-drawer__body {
    flex: 1 1 auto;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 16px;
    /* --ai-bg. The design flags this as outside the V2 primitives (Gray/50 is #f5f5f5) and uses the
     * literal anyway, so the white cards inside read as raised. Do not "correct" it to #f5f5f5. */
    background: #fafafa;
}

/* ── Sections ────────────────────────────────────────────────────────────────── */

.ai-review__section {
    margin: 0 0 20px;   /* --space-lg, the design's gap between sections */
}

.ai-review__section:last-child {
    margin-bottom: 0;
}

.ai-review__heading {
    margin: 0 0 10px;   /* --space-sm-plus */
    font-size: 16px;    /* --subtitle-lg */
    line-height: 1.6;
    font-weight: 700;
    color: #191919;     /* --text-primary */
}

/* ── Corrections ─────────────────────────────────────────────────────────────── */

.ai-review__correction {
    margin: 0 0 10px;                /* --space-sm-plus */
    padding: 16px;
    border: 1px solid #eeeeee;       /* --v2-border-faint */
    border-radius: 12px;             /* --v2-radius-lg */
    background: #fff;
}

.ai-review__correction:last-child {
    margin-bottom: 0;
}

/* 復習ポイント — only rendered when is_recurring is true (FR-025).
 *
 * brand-secondary SOLID (#092846) with white text, 14.96:1. The design's reasoning is worth keeping:
 * red/green/orange are already spoken for by wrong/right/brand, so a solid navy fill is the only
 * remaining way to make one card in four stand out — a pale tint with matching text is the treatment for
 * badges that appear several at a time. */
.ai-review__badge {
    display: inline-block;
    margin: 0 0 8px;
    padding: 4px 8px;
    border-radius: 9999px;
    background: #092846;
    color: #fff;
    font-size: 12px;
    line-height: 1.4;
    font-weight: 700;
}

/* Both lines carry the SAME padding and border width so the ✕ and ✓ glyphs and the sentences after them
 * start at the same x. */
.ai-review__line {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 0 0 8px;
    padding: 8px 10px;
    border: 1px solid transparent;
    border-radius: 10px;
    font-size: 15px;
    line-height: 1.7;
}

/* BOXED — the design's `.corr_line.before.is-boxed` variant (BOX_BEFORE = True, the Codecademy / Uxcel
 * shape), which is what Moriya's comp shows.
 *
 * The design ships both: unboxed leaves this border transparent as an invisible alignment box, boxed
 * fills it so the pair reads as symmetric. I implemented the unboxed default first — the toggle is a
 * commented switch in review.css, easy to miss. The symmetric version is the approved one.
 *
 * Both status colours are V2's palest tints (Red/50, LightGreen/50) precisely so four stacked corrections
 * do not read as a report card of red. */
.ai-review__line--bad {
    background: #fff9f9;             /* --v2-error-bg, Red/50 */
    border-color: #ff3535;           /* --v2-error-border */
    color: #767676;                  /* --v2-text-secondary */
}

.ai-review__line--good {
    background: #f3fcf4;             /* --v2-success-bg, LightGreen/50 */
    border-color: #32d74b;           /* --v2-success-border */
    color: #191919;                  /* --v2-text-primary */
    font-weight: 700;
}

.ai-review__mark {
    flex: 0 0 auto;
    margin-top: 2px;
}

.ai-review__mark--bad  { color: #d92929; }   /* --v2-error-fg */
.ai-review__mark--good { color: #1e8b2e; }   /* --v2-success-fg */

.ai-review__text {
    flex: 1 1 auto;
    min-width: 0;
    word-break: break-word;
}

/* ── Changed words within the correction pair ─────────────────────────────────────
 *
 * Written by _diffPair() in ai_review.js. Colour and weight ONLY — no underline, no strikethrough.
 *
 * The design considered a strikethrough on the original and rejected it: with a ✕ already at the start of
 * the line the strikethrough is redundant, and its research found that where it IS used (Grammarly,
 * WRITER, Duolingo MAX, GitBook, Ditto, Fibery) the rule is body-coloured, not red — only Contractbook
 * used red. Marking the whole sentence makes the words that were correct look wrong. */
.ai-review__del {
    color: #d92929;                  /* --v2-error-fg */
    font-weight: 700;
}

/* No extra weight: the corrected line is already 700, so the colour alone carries it. */
.ai-review__ins {
    color: #1e8b2e;                  /* --v2-success-fg */
}

.ai-review__note {
    margin: 8px 0 0;
    font-size: 13px;                 /* --caption-lg */
    line-height: 1.7;
    color: #767676;
}

/* corrections[].rule — displayed per the design, which closes the definition's open §7 question.
 * It is ENGLISH by design (§A.1: it is the cross-lesson matching key, so it needs one fixed
 * language), which means it will sit in English next to Japanese explanatory text. That is intended.
 * Styled as a muted chip so it reads as metadata rather than as prose the member must parse. */
.ai-review__rule {
    display: inline-block;
    margin: 8px 0 0;
    padding: 6px 8px;
    border-radius: 4px;              /* --v2-radius-sm */
    background: #f5f5f5;             /* --v2-surface-subtle */
    color: #767676;
    font-size: 12px;
    line-height: 1.4;
    word-break: break-word;
}

/* ── What to Focus On Next ───────────────────────────────────────────────────── */

/* Orange/50 fill inside a brand-primary border. The design notes it considered the purple treatment from
 * the reference attachment and moved to orange because V2's Purple is already committed to corporate
 * plan surfaces — a purple card here would collide with that meaning. */
.ai-review__advice {
    margin: 0;
    padding: 16px;
    list-style: none;
    border: 1px solid #f1890e;       /* --v2-brand-primary */
    border-radius: 12px;             /* --v2-radius-lg */
    background: #fcf8f3;             /* --v2-orange-50 */
}

.ai-review__advice li {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 0 0 10px;                /* --space-sm-plus */
    font-size: 14px;                 /* --body-sm */
    line-height: 1.7;
    color: #191919;
}

.ai-review__advice li:last-child {
    margin-bottom: 0;
}

/* 24px check_circle (V2 icon-base) in brand-primary. No margin-top: at 24px the glyph's own optical
 * centre already lines up with a 1.7 line-height first line. */
.ai-review__advice-mark {
    flex: 0 0 auto;
    color: #f1890e;
}

/* ── Lesson Summary ──────────────────────────────────────────────────────────── */

.ai-review__summary {
    margin: 0;
    font-size: 14px;                 /* --body-sm */
    line-height: 1.8;
    color: #191919;
}

/* ── Use These Next Time ─────────────────────────────────────────────────────── */

.ai-review__words {
    margin: 0;
    padding: 0;
    list-style: none;
}

/* The WHOLE TILE is the hit target — which is what the design intended all along: it fixes the speaker
 * icon to the left edge specifically to read as "tap the row to hear it" (Duolingo / Babbel pattern,
 * RESEARCH-mobbin P7). Moriya's request was really a request to finish this. */
.ai-review__word-tile {
    display: flex;
    align-items: flex-start;
    gap: 10px;                       /* --space-sm-plus */
    margin: 0 0 10px;
    padding: 10px 12px;
    border: 1px solid #eeeeee;       /* --v2-border-faint */
    border-radius: 12px;             /* --v2-radius-lg */
    background: #fff;
    cursor: pointer;
    transition: none;                /* the design specifies instant state changes here */
}

/* ⚠️ HOVER AND PLAYING USE background-image, NOT background.
 *
 * The overlay is brand-primary at 8% alpha. Setting `background` to that translucent colour REPLACES the
 * white fill, so the 8% orange composites against the #fafafa body behind it and the tile turns grey-ish
 * instead of warm. Layering it as a gradient image keeps the white underneath. The design calls this out
 * explicitly; it is not a stylistic preference. */
.ai-review__word-tile:hover {
    background-image: linear-gradient(rgba(241, 137, 14, .08), rgba(241, 137, 14, .08));
}

.ai-review__word-tile:last-child {
    margin-bottom: 0;
}

/* Playing: same 8% overlay plus a brand-primary border. The WORD ITSELF is deliberately not recoloured —
 * brand-primary text on a pale orange fill measures 2.35:1 and fails AA. State is carried by the border,
 * the fill and the speaker glyph instead. */
.ai-review__word-tile.is-speaking {
    background-image: linear-gradient(rgba(241, 137, 14, .08), rgba(241, 137, 14, .08));
    border-color: #f1890e;
}

/* The speaker is 32×32 and vertically CENTRED on the tile, not top-aligned with the word — it belongs to
 * the whole row, which is the visual cue that the whole row is tappable. */
.ai-review__speak {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    align-self: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border: none;
    border-radius: 9999px;
    background: transparent;
    color: #6b6b6b;                  /* --text-secondary, matching speaker.svg's own #6b6b6b */
    cursor: pointer;
}

/* speaker_on.svg is the same glyph in brand-primary; recolouring via currentColor avoids inlining a
 * second copy of the path into every tile. */
.ai-review__word-tile.is-speaking .ai-review__speak {
    color: #f1890e;
}

.ai-review__speak:focus-visible {
    outline: 2px solid #f1890e;
    outline-offset: 1px;
}

/* FR-042: sized for SHORT PHRASES, which are the preferred content — not single words. Nothing here
 * truncates or clamps: an item like "come up with an idea" wraps rather than being cut off. JA's
 * equivalent tiles were sized for single Japanese words; that sizing does not transfer. */
.ai-review__word-body {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.ai-review__word {
    font-size: 15px;
    line-height: 1.5;
    font-weight: 700;
    color: #191919;
    word-break: break-word;
}

.ai-review__meaning {
    font-size: 13px;                 /* --caption-lg */
    line-height: 1.5;
    color: #6b6b6b;                  /* --text-secondary */
    word-break: break-word;
}

/* Same colour as the meaning, one step smaller. The previous #94a3b8 was a third, lighter grey that the
 * design does not use — it made the example look disabled. */
.ai-review__example {
    font-size: 12px;
    line-height: 1.5;
    color: #6b6b6b;
    word-break: break-word;
}

/* ── Insufficient data ───────────────────────────────────────────────────────── */

/* Shown ONLY when has_enough_data is false. A lesson with plenty of speech and no mistakes simply
 * omits its empty sections — it must never show this notice, which would wrongly tell the member
 * they barely spoke (FR-030a). */
.ai-review__notice {
    margin: 0;
    padding: 12px;                   /* --space-md */
    border-radius: 8px;              /* --radius-md */
    background: #f5f5f5;             /* --surface-subtle */
    color: #6b6b6b;
    font-size: 13px;                 /* --caption-lg */
    line-height: 1.7;
}

/* ── Loading ─────────────────────────────────────────────────────────────────── */

/* Spinner ABOVE the text, not beside it — the design's markup is `.spinner` then `<p>` inside
 * `.ai_loading`, i.e. a column. An earlier version used a flex ROW, which put a small orange ring next
 * to the label on one line. */
.ai-review__loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    gap: 12px;                       /* --space-md */
    padding: 40px 0;
    color: #6b6b6b;
    font-size: 14px;                 /* --body-sm */
    line-height: 1.6;
    text-align: center;
}

/* NEUTRAL, not brand orange — the orange belongs to the header and the trigger, and a loading state is
 * not where the eye should be pulled. Matches the existing #transcription-spinner so the two spinners in
 * this panel look like one component: 36px, 3px #ccc ring with a #999 BOTTOM segment, 0.9s. */
.ai-review__spinner {
    width: 36px;
    height: 36px;
    border: 3px solid #ccc;
    border-bottom-color: #999;
    border-radius: 50%;
    animation: ai-review-spin .9s linear infinite;
}

@keyframes ai-review-spin {
    to { transform: rotate(360deg); }
}

/* The spinner is the one animation in this feature, and it communicates real state (a request is in
 * flight), so it is suppressed rather than removed under reduced-motion. */
@media (prefers-reduced-motion: reduce) {
    .ai-review__spinner {
        animation-duration: 2.4s;
    }
    .ai-review-fab {
        transition: none;
    }
}

/* ── Error states ────────────────────────────────────────────────────────────── */

.ai-review__error-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;                       /* --space-md */
    padding: 40px 20px;              /* --space-lg horizontally */
    text-align: center;
}

.ai-review__error {
    margin: 0;                       /* the flex gap above owns the spacing now */
    color: #d93025;                  /* --red-600 */
    font-size: 14px;                 /* --body-sm */
    line-height: 1.7;
}

/* FR-031: every failure is retryable — nothing was stored, so a later attempt can still succeed.
 * Height-driven at 40px so it matches the trigger pill's metrics. */
.ai-review__retry {
    height: 40px;
    padding: 0 20px;                 /* --space-lg */
    border: 1px solid #cccccc;       /* --border-strong */
    border-radius: 9999px;
    background: #fff;
    color: #191919;
    font-size: 14px;                 /* --subtitle-sm */
    font-weight: 700;
    cursor: pointer;
}

.ai-review__retry:hover {
    background: #f5f5f5;             /* --surface-subtle */
}

.ai-review__retry:focus-visible {
    outline: 2px solid #f1890e;
    outline-offset: 2px;
}

/* ── Screen-reader-only text ─────────────────────────────────────────────────── */

/* Carries the 「元の発話」/「訂正」 labels the design replaces with ✕/✓ icons. The labels still exist in
 * all eight locales and are read out here, so the icons are not unlabelled for assistive tech. */
.ai-review__sr {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}
