/* ==========================================================================
   levels-blog — single-post furniture: reading progress bar + sticky rail
   --------------------------------------------------------------------------
   Loaded on is_singular('post') only, after blocks.css (which owns the
   --lvl-blog-* token layer this file reuses).

   The same two Oxygen cascade traps apply as in blocks.css:

     1. Oxygen prints an inline <style> in the body that underlines links with
        !important. The rail is navigation, not prose — its links restate
        `text-decoration: none !important`.
     2. --text-color / --bg-color do NOT flip with dark mode (Oxygen switches
        the theme with a `body.dark-mode { color: … }` rule). Nothing here sets
        a text colour from them: rail text is `inherit`, and the accent colours
        come from the --lvl-blog-* layer, which is declared per mode.

   Measured on the dev box (template #774, viewport 1600):
     .ct-inner-content        1000px wide, centred, overflow visible
     .ct-section-inner-wrap   viewport − 60px (30px padding per side)
     .oxy-header-wrapper      96px tall, position: fixed once scrolled,
                              z-index 2147483640, top = admin-bar height
     body                     overflow-x: hidden  → a bleed past the viewport
                              is clipped, it never adds a horizontal scrollbar
   ========================================================================== */

:root {
    /* Oxygen's sticky header is 96px and parks below the admin bar. */
    --lvl-header-h: 6rem;
    --lvl-header-offset: calc(var(--wp-admin--admin-bar--height, 0px) + var(--lvl-header-h));

    --lvl-rail-w: 280px;
    --lvl-rail-gap: 3rem;
}

/* --------------------------------------------------------------------------
   Reading progress
   -------------------------------------------------------------------------- */

/*
 * Above Oxygen's sticky header, which sits at z-index 2147483640 — hence the
 * +1. Parked under the admin bar for logged-in users, at the very top edge for
 * everyone else.
 */
.lvl-progress {
    position: fixed;
    top: var(--wp-admin--admin-bar--height, 0px);
    left: 0;
    right: 0;
    height: 4px;
    z-index: 2147483641;
    background: var(--lvl-blog-border-soft, rgba(243, 245, 252, 0.09));
    pointer-events: none;
}

.lvl-progress__bar {
    width: 0;
    height: 100%;
    transform-origin: left center;
    background: linear-gradient(90deg, var(--brand-rose, #ce7aa3), var(--brand-violet, #8d8bd9));
    transition: width 80ms linear;
}

/* --------------------------------------------------------------------------
   Layout
   --------------------------------------------------------------------------
   Below the breakpoint the rail is a normal block ABOVE the article (order:
   -1) — a collapsed TOC plus the share row, reachable without scrolling to
   the end.
   -------------------------------------------------------------------------- */

.lvl-post {
    display: flex;
    flex-direction: column;
    /* Room between the last content block and the Oxygen author section
       that follows (its avatar overlaps 100px upward into this space). */
    padding-block-end: clamp(3rem, 8vw, 6rem);
}

.lvl-post__body {
    min-width: 0;
}

.lvl-post__rail {
    order: -1;
    min-width: 0;
    margin-block-end: var(--lvl-blog-rhythm, 2rem);
    color: inherit;
}

@media (min-width: 1360px) {
    /*
     * The rail lives OUTSIDE the 1000px column so the reading measure is not
     * narrowed. How far it may reach is not a constant: the free space to the
     * right of the centred column is (100vw − 1000px) / 2, and the section
     * already spends 30px of it on its own padding. --lvl-rail-bleed takes as
     * much of that as it can, up to the full rail width + gap, and the grid
     * absorbs whatever is left by shortening the prose column. So:
     *
     *   1360px viewport → 140px bleed, prose 812px  (rail partly inset)
     *   1600px viewport → 260px bleed, prose 932px
     *   ≥1760px         → 328px bleed, prose 1000px (rail fully outside)
     *
     * No breakpoint juggling, and it can never be clipped by the viewport.
     */
    .lvl-post {
        --lvl-rail-bleed: clamp(
            0px,
            calc((100vw - 1000px) / 2 - 2.5rem),
            calc(var(--lvl-rail-w) + var(--lvl-rail-gap))
        );
        display: grid;
        grid-template-columns: minmax(0, 1fr) var(--lvl-rail-w);
        column-gap: var(--lvl-rail-gap);
        align-items: start;
        width: calc(100% + var(--lvl-rail-bleed));
        margin-inline-end: calc(-1 * var(--lvl-rail-bleed));
    }

    .lvl-post__rail {
        order: 0;
        /* `align-self: start` is required: a stretched grid item has no room
           to slide inside its row, and sticky silently does nothing. */
        align-self: start;
        position: sticky;
        top: calc(var(--lvl-header-offset) + 1.5rem);
        margin-block-end: 0;
        max-height: calc(100vh - var(--lvl-header-offset) - 3rem);
        overflow-y: auto;
        /* room for focus rings so they are not clipped by the scroll box */
        padding-inline: 0.25rem;
        margin-inline: -0.25rem;
        scrollbar-width: thin;
        scrollbar-color: var(--lvl-blog-border, rgba(243, 245, 252, 0.16)) transparent;
    }

    .lvl-post__rail::-webkit-scrollbar {
        width: 6px;
    }

    .lvl-post__rail::-webkit-scrollbar-thumb {
        background: var(--lvl-blog-border, rgba(243, 245, 252, 0.16));
        border-radius: 3px;
    }

    /* The inline lvl/toc block is a duplicate of the rail TOC — but only when
       the rail actually built one. */
    .lvl-post--has-toc .lvl-post__body > .lvl-toc {
        display: none;
    }
}

/* Below the breakpoint the rail TOC replaces the inline block too, so the
   reader never meets the same list twice. */
@media (max-width: 1359.98px) {
    .lvl-post--has-toc .lvl-post__body > .lvl-toc {
        display: none;
    }
}

/*
 * Anchor jumps must clear the fixed header. Scoped to the body so it cannot
 * leak onto Oxygen's own headings elsewhere on the page.
 */
.lvl-post__body :is(h2, h3, h4)[id] {
    scroll-margin-top: calc(var(--lvl-header-offset) + 1rem);
}

/* --------------------------------------------------------------------------
   Reading measure
   --------------------------------------------------------------------------
   The Oxygen column is 1000px and the body type is 20px — around 100
   characters a line, well past the point where the eye loses the start of the
   next one. Running text is capped at 68ch and stays flush left in the column;
   anything whose job is to be wide — tables, code, images, figures, the
   key-figure grid, the video, related links, the case card — keeps the full
   width. `:where()` so this adds no specificity and a block's own rule still
   wins.
   -------------------------------------------------------------------------- */

.lvl-post__body > :where(p, ul, ol, blockquote, h2, h3, h4, .lvl-tldr, .lvl-info-box, .lvl-checklist, .lvl-faq, .lvl-cta-box, .lvl-author-box) {
    max-width: 68ch;
    margin-inline-end: auto;
}

/* alignwide/alignfull opt out of the measure on purpose. */
.lvl-post__body > :where(.alignwide, .alignfull, .aligncenter) {
    max-width: none;
}

/* --------------------------------------------------------------------------
   Breadcrumb
   -------------------------------------------------------------------------- */

.lvl-crumbs {
    margin: 0 0 var(--lvl-blog-rhythm, 2rem);
    font-size: var(--base-xs, 0.875rem);
    line-height: 1.4;
    color: inherit;
}

.lvl-crumbs__list {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 0.5rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.lvl-crumbs__item {
    margin: 0;
    padding: 0;
    opacity: 0.72;
}

.lvl-crumbs__item + .lvl-crumbs__item::before {
    content: "\203A";
    margin-inline-end: 0.5rem;
    opacity: 0.6;
}

/* Oxygen's inline <style> underlines links with !important — the crumb only
   underlines on hover/focus. */
.lvl-crumbs__link {
    color: inherit;
    text-decoration: none !important;
    border-bottom: 1px solid transparent;
}

.lvl-crumbs__link:hover,
.lvl-crumbs__link:focus-visible {
    border-bottom-color: currentColor;
}

.lvl-crumbs__link:focus-visible {
    outline: 2px solid var(--lvl-blog-accent, #ce7aa3);
    outline-offset: 2px;
    border-radius: var(--lvl-blog-radius-sm, 0.375rem);
}

/* --------------------------------------------------------------------------
   Rail: table of contents
   -------------------------------------------------------------------------- */

.lvl-rail-toc {
    margin: 0;
}

.lvl-rail-toc__title {
    margin: 0 0 0.75rem;
    font-size: var(--base-xs, 0.875rem);
    line-height: 1.3;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    /* 0.72 keeps the blended label at 7.8:1 — WCAG AAA in both modes. */
    opacity: 0.72;
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    /* 44px touch target while collapsed on small screens */
    min-height: 2.75rem;
}

.lvl-rail-toc__title::-webkit-details-marker {
    display: none;
}

.lvl-rail-toc__title::after {
    content: "";
    flex: none;
    width: 0.5rem;
    height: 0.5rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg);
    transition: transform 0.25s var(--lvl-blog-ease, ease);
}

.lvl-rail-toc__details:not([open]) > .lvl-rail-toc__title::after {
    transform: rotate(-45deg);
}

.lvl-rail-toc__list,
.lvl-rail-toc__sublist {
    margin: 0;
    padding: 0;
    list-style: none;
}

.lvl-rail-toc__item {
    margin: 0;
    padding: 0;
}

/* The accent rail behind the links; the active link paints its own segment. */
.lvl-rail-toc__link {
    display: block;
    padding: 0.625rem 0 0.625rem 0.875rem;
    /* 44px minimum touch target: 2 × 0.625rem padding + 1.45 line-height on
       the ~0.95rem type clears it, and min-height guarantees it. */
    min-height: 2.75rem;
    box-sizing: border-box;
    font-size: var(--base-20, 0.9375rem);
    line-height: 1.45;
    color: inherit;
    /* Oxygen's inline <style> underlines every link with !important. */
    text-decoration: none !important;
    border-inline-start: 2px solid var(--lvl-blog-border-soft, rgba(243, 245, 252, 0.09));
    transition: color 0.18s var(--lvl-blog-ease, ease), border-color 0.18s var(--lvl-blog-ease, ease);
}

.lvl-rail-toc__item--h3 .lvl-rail-toc__link {
    padding-inline-start: 1.75rem;
    font-size: var(--base-xs, 0.875rem);
    opacity: 0.85;
}

/*
 * Scoped through .lvl-post__rail on purpose. Oxygen styles links with a
 * inline <style> in the body — the same one that underlines links — and it
 * writes the link colour with !important. Measured: without !important the
 * accent colour is silently dropped while the stripe from the SAME rule does
 * land, so the rule looks like it applies. Oxygen's own stylesheets are
 * CORS-opaque, so this can only be found by measuring the computed value.
 */
.lvl-post__rail .lvl-rail-toc__link:hover {
    color: var(--lvl-blog-accent-text, #7d3556) !important;
    border-inline-start-color: var(--lvl-blog-border, rgba(243, 245, 252, 0.16));
}

/* single.js sets aria-current="location" — "this is the section you are in",
   which is not the same claim as aria-current="true". */
.lvl-post__rail .lvl-rail-toc__link[aria-current] {
    color: var(--lvl-blog-accent-text, #7d3556) !important;
    border-inline-start-color: var(--lvl-blog-accent, #ce7aa3);
}

/* Collapsed state on small screens gets a frame, so it reads as one control. */
@media (max-width: 1359.98px) {
    .lvl-rail-toc {
        padding: 0.75rem var(--lvl-blog-pad, 1.25rem);
        border: 1px solid var(--lvl-blog-border, rgba(243, 245, 252, 0.16));
        border-radius: var(--lvl-blog-radius, 0.75rem);
        background: var(--lvl-blog-surface, rgba(243, 245, 252, 0.05));
    }

    .lvl-rail-toc__details[open] > .lvl-rail-toc__title {
        margin-bottom: 0.5rem;
    }
}

/* On desktop the title is a label, not a disclosure — single.js forces the
   details open and takes the summary out of the tab order there. */
@media (min-width: 1360px) {
    .lvl-rail-toc__title {
        cursor: default;
        min-height: 0;
    }

    .lvl-rail-toc__title::after {
        display: none;
    }
}

/* --------------------------------------------------------------------------
   Rail: share
   -------------------------------------------------------------------------- */

.lvl-rail-share {
    margin-top: 1.75rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--lvl-blog-border-soft, rgba(243, 245, 252, 0.09));
}

.lvl-rail-share__title {
    margin: 0 0 0.5rem;
    font-size: var(--base-xs, 0.875rem);
    line-height: 1.3;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    /* 0.72 keeps the blended label at 7.8:1 — WCAG AAA in both modes. */
    opacity: 0.72;
}

.lvl-rail-share__list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.lvl-rail-share__item {
    margin: 0;
    padding: 0;
}

.lvl-rail-share__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* 44 × 44 touch target (WCAG 2.2 AA target size). */
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border: 0;
    border-radius: 9999px;
    background: transparent;
    color: inherit;
    cursor: pointer;
    text-decoration: none !important;
    transition: background-color 0.18s var(--lvl-blog-ease, ease), color 0.18s var(--lvl-blog-ease, ease);
}

.lvl-post__rail .lvl-rail-share__btn:hover {
    background: var(--lvl-blog-surface-strong, rgba(243, 245, 252, 0.09));
    color: var(--lvl-blog-accent-text, #7d3556) !important;
}

.lvl-rail-share__icon {
    display: block;
    width: 20px;
    height: 20px;
    fill: currentColor;
}

.lvl-post__rail .lvl-rail-share__copy.is-copied {
    color: var(--lvl-blog-tip, #8cb369) !important;
}

.lvl-rail-share__feedback {
    margin: 0.25rem 0 0;
    min-height: 1.25rem;
    font-size: var(--base-xs, 0.8125rem);
    line-height: 1.25rem;
    opacity: 0.8;
}

/* --------------------------------------------------------------------------
   Focus visibility (WCAG) — never removed
   -------------------------------------------------------------------------- */

.lvl-rail-toc__link:focus-visible,
.lvl-rail-toc__title:focus-visible,
.lvl-rail-share__btn:focus-visible {
    outline: 2px solid var(--lvl-blog-accent, #ce7aa3);
    outline-offset: 2px;
    border-radius: var(--lvl-blog-radius-sm, 0.375rem);
}

/* --------------------------------------------------------------------------
   Reduced motion
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
    .lvl-progress__bar,
    .lvl-rail-toc__title::after,
    .lvl-rail-toc__link,
    .lvl-rail-share__btn {
        transition: none !important;
    }
}

/* ---------------------------------------------------------------------------
   Prose rhythm — more air before a new section. Oxygen's global heading margin
   is symmetric, so an H2 sits as close to the paragraph above as to its own
   text. Headings that open a section get a clear top margin; the first child
   keeps none. `:where()` keeps specificity at zero.
   ------------------------------------------------------------------------- */
.lvl-post__body > :where(h2) { margin-block-start: 3rem; margin-block-end: 1rem; }
.lvl-post__body > :where(h3) { margin-block-start: 2.25rem; margin-block-end: 0.75rem; }
.lvl-post__body > :where(h4) { margin-block-start: 1.75rem; margin-block-end: 0.5rem; }
.lvl-post__body > :where(h2, h3, h4):first-child { margin-block-start: 0; }
.lvl-post__body > :where(h2, h3, h4) + :where(h2, h3, h4) { margin-block-start: 1rem; }

/* ---------------------------------------------------------------------------
   Responsive tables — core/table renders <figure class="wp-block-table">
   with `has-fixed-layout`, which squeezes cells to a few characters on phones.
   Let the table keep a readable minimum width and scroll the figure instead.
   The figure gets tabindex=0 from single.js so keyboard users can scroll it.
   ------------------------------------------------------------------------- */
.lvl-post__body .wp-block-table {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    max-width: 100%;
    scrollbar-width: thin;
}
.lvl-post__body .wp-block-table table {
    table-layout: auto;
    min-width: 36rem;
    width: 100%;
}
.lvl-post__body .wp-block-table.has-fixed-layout table,
.lvl-post__body .wp-block-table table.has-fixed-layout {
    table-layout: auto;
}
.lvl-post__body .wp-block-table td,
.lvl-post__body .wp-block-table th {
    white-space: normal;
    word-break: normal;
    hyphens: manual;
    padding: 0.6em 0.9em;
    vertical-align: top;
}
.lvl-post__body .wp-block-table:focus-visible {
    outline: 2px solid var(--lvl-blog-accent-text, #7d3556);
    outline-offset: 3px;
}
@media (max-width: 767px) {
    .lvl-post__body .wp-block-table { margin-inline: calc(-1 * var(--lvl-post-gutter, 0px)); padding-inline: var(--lvl-post-gutter, 0px); }
}

/* ---------------------------------------------------------------------------
   Heading scale inside the article. Oxygen sizes H2 with `--h2-base`
   (clamp on cqw — collapses to 1.4rem in the 1000 px column on phones) and
   H3 with a fixed 2rem, so on 390 px an H3 renders larger than its H2.
   Re-pin the prose scale on vw so the hierarchy holds at every width.
   ------------------------------------------------------------------------- */
.lvl-post__body > :where(h2) { font-size: clamp(1.75rem, 1.15rem + 2.4vw, 2.5rem); line-height: 1.2; }
.lvl-post__body > :where(h3) { font-size: clamp(1.35rem, 1.05rem + 1.2vw, 1.85rem); line-height: 1.3; }
.lvl-post__body > :where(h4) { font-size: clamp(1.15rem, 1rem + 0.6vw, 1.4rem); line-height: 1.35; }
