/* ═══════════════════════════════════════════════════════
   Web.Frontend — App-specific styles
   Moved from Shared/Frontend/wwwroot/app.css
═══════════════════════════════════════════════════════ */

:root {
    /* Font sizes */
    --text-2xs: calc(0.625rem * var(--font-scale));

    /* Surface overlays. --tk-surface-subtle is the fourth of these and lives in the shared
       stylesheet, which defines --surface-input from it. */
    --tk-surface-hover:        rgba(255, 255, 255, 0.06);
    --tk-surface-active:       rgba(255, 255, 255, 0.08);

    /* Solid raised surface — the lift behind an active connected tab and its joined panel. */
    --tk-surface-lift:         #211e26;

    /* Icon color on filled backgrounds */
    --tk-icon-on-fill: white;

    /* Radius */
    --tk-radius-full: 9999px;
}

/* Stretch intermediate wrappers (ErrorBoundary, TkRenderScope) so h-full + overflow-y-auto works inside the flex column. */
.web-app > * { display: flex; flex-direction: column; flex: 1; min-height: 0; }

/* The nav's build-feed action, used by the landing page and the language-routed marketing pages.
   App-level because two components need it: language and sign-in stay in the nav on a phone, but a
   third button would crowd the header, and every page carrying it has a CTA further down. */
.landing-nav-cta {
    display: inline-flex;
}

@media (max-width: 767px) {
    .landing-nav-cta {
        display: none;
    }
}

/* Landing static controls and placeholders shared by the landing page and its feature components. */
.landing-link-nav {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    font-size: 0.82rem;
    color: var(--text-mid);
    text-decoration: none;
    transition: color 0.2s;
}

.landing-link-nav :where(a) {
    color: inherit;
    text-decoration: none;
    transition: inherit;
}

.landing-tab-input {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Landing phone carousels — shared by the frustration quotes, the space stat cards, and the
   feature chip rail. Edge-bled
   past the 1.25rem mobile container padding so cards scroll under the page gutter, with the
   next card peeking to signal swipeability. div.landing lifts specificity (0,2,1) over the
   components' scoped desktop layout rules (0,2,0) — the static landing page has no .web-app
   wrapper, so the .tk-fill-inverted trick below doesn't apply here. */
@media (max-width: 767px) {
    div.landing .landing-snap-row {
        display: flex;
        gap: 0.875rem;
        overflow-x: auto;
        margin-inline: -1.25rem;
        padding-inline: 1.25rem;
        scroll-snap-type: x mandatory;
        scroll-padding-inline: 1.25rem;
        overscroll-behavior-x: contain;
        scrollbar-width: none;
    }

    div.landing .landing-snap-row::-webkit-scrollbar {
        display: none;
    }

    div.landing .landing-snap-row > * {
        flex-shrink: 0;
        scroll-snap-align: start;
    }
}


.tk-sheet-choices {
    display: flex;
    flex-direction: column;
    padding: 0.25rem 0 0.5rem;
}

.tk-sheet-choice {
    display: flex;
    align-items: center;
    gap: 0.875rem;
    padding: 0.875rem 0.5rem;
    color: var(--text-mid);
    font-size: var(--text-base);
    font-weight: 500;
    text-align: left;
    background: transparent;
    border: none;
    cursor: pointer;
}

.tk-sheet-choice + .tk-sheet-choice {
    border-top: 1px solid var(--border);
}

.tk-sheet-choice-icon {
    display: flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    color: var(--text-mid);
}


.tk-action-btn {
    display: inline-flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    gap: 0.375rem;
    padding: 0.375rem 0.875rem 0.375rem 0.75rem;
    font-size: var(--text-xs);
    font-weight: 500;
    line-height: 1rem;
    border-radius: var(--tk-radius-md);
    cursor: pointer;
}

/* ─── Segmented control ───
   The look is app-level because two components render it: TkSegmentedControl (buttons + click
   handlers) and TkStaticSegmentedControl (radios + :checked), which is what the static-SSR
   public pages have to use. Both must stay visually identical, so the active state below lists
   the two selectors together rather than defining the same look twice. */

.tk-segmented {
    display: inline-flex;
    gap: 0.25rem;
    padding: 0.1875rem;
    border-radius: var(--tk-radius-lg);
    background: var(--surface-input);
    border: 1px solid var(--border);
}

.tk-segmented-option {
    padding: 0.3125rem 1rem;
    border-radius: var(--tk-radius-md);
    font-size: var(--text-sm);
    font-weight: 400;
    color: var(--text-mid);
    background: transparent;
    border: none;
    cursor: pointer;
    transition: color 0.15s, background 0.15s;
    white-space: nowrap;
}

/* Only options carrying an icon become flex rows; a plain label keeps its inline text box so the
   existing controls lay out exactly as before. */
.tk-segmented-option--icon {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
}

.tk-segmented-option-icon {
    display: inline-flex;
    flex-shrink: 0;
}

/* Shared resting hover for both variants: an inactive option brightens text-mid → text-hi. */
.tk-segmented-option:hover:not(.tk-segmented-option--active):not(:disabled) {
    color: var(--text-hi);
}

.tk-segmented-option--active,
.tk-segmented-input:checked + .tk-segmented-option {
    color: var(--text-hi);
    background: rgba(255, 255, 255, 0.08);
}

/* Active-state polish: the already-selected option brightens its bg on hover, distinct from the resting text hover above. */
.tk-segmented-option--active:hover,
.tk-segmented-input:checked + .tk-segmented-option:hover {
    background: rgba(255, 255, 255, 0.1);
}

.tk-segmented-option:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* ── Dense variant ───────────────────────────────────────────────────────
   The default look — bordered container, same surface — with everything inside it pulled in. For a
   control that repeats down every row of a list, where the control's height is what sets the row's.
   Not the compact variant below: that swaps the chrome for borderless pills, which reads as a
   different control rather than the same one at a smaller size.
   Takes the input corner rather than radius-lg: at this height lg is too round for the box, and the
   control sits on the same line as the row's other controls, which are input-shaped. */
.tk-segmented--dense {
    gap: 0.125rem;
    padding: 0.125rem;
    border-radius: var(--tk-radius-input);
}

/* The label's leading is what sets this control's height once the meter has shrunk below it, so it
   is pulled in too — otherwise the smaller glyph buys nothing. */
.tk-segmented--dense .tk-segmented-option {
    padding: 0.25rem 0.625rem;
    font-size: var(--text-sm);
    line-height: 1.25;
}

.tk-segmented--dense .tk-segmented-option--icon {
    gap: 0.375rem;
}

/* ── Compact variant ─────────────────────────────────────────────────────
   Denser, fully-rounded pills with no container border, for tight rows
   (source volume, coverage levels). */
.tk-segmented--compact {
    gap: 0.125rem;
    padding: 0.125rem;
    border: none;
    border-radius: var(--tk-radius-full);
}

.tk-segmented--compact .tk-segmented-option {
    padding: 0.25rem 0.625rem;
    border-radius: var(--tk-radius-full);
    font-size: var(--text-xs);
    font-weight: 500;
    letter-spacing: -0.01em;
}

.tk-segmented--compact .tk-segmented-option--active,
.tk-segmented--compact .tk-segmented-input:checked + .tk-segmented-option {
    color: var(--text-hi);
    background: color-mix(in srgb, var(--text-hi) 10%, transparent);
}

/* ── Micro variant ───────────────────────────────────────────────────────
   Compact shrunk once more, for the currency toggle tucked into a plan card's top-right corner
   where a compact pill still crowds the price beside it. */
.tk-segmented--micro .tk-segmented-option {
    padding: 0.15rem 0.5rem;
    font-size: var(--text-2xs);
}

/* Active option carrying an inline accessory (e.g. the source reset control). */
.tk-segmented-option--accessory {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
}

.tk-segmented--compact .tk-segmented-option--accessory {
    padding-right: 0.1875rem;
}

/* An option carrying a level meter (coverage levels, source volumes). The control brightens the
   label on hover; its glyph has to come with it, or the meter sits there dimmed while the word
   beside it lights up. Off is excluded: it marks being set by going dark, so hover must not light
   it back up. */
.tk-segmented-option:hover .tk-level-meter--bars {
    opacity: 1;
}

.tk-segmented-option:hover .tk-level-meter--off:not(.is-emphasised) {
    color: var(--text-hi);
}

/* ─── Marketing pages ───
   Shared furniture for every language-routed marketing page (how-it-works, spoiler-free,
   pricing, tv, gaming). Each page keeps its own root class for the sections only it has, and
   for the two width knobs above. The pieces here are: the
   shell, the hero, section headings, the fact-card grid, the cross-links footnote, and the
   closing CTA panel. App-level because more than one page renders each of these; anything a
   single page owns stays in that page's scoped CSS. */

.mkt-page {
    min-height: 100%;
    overflow-y: auto;
    background: var(--surface-app);
    color: var(--text-mid);
}

/* The default is wider than a plain reading column: the loop's rail takes a column of its own,
   and at 960px that left the copy at roughly 300px, which broke every paragraph into ragged
   three-word lines. Pages without a rail set a narrower width on their own root. */
.mkt-main {
    width: min(100%, var(--mkt-main-width, 1100px));
    margin: 0 auto;
    padding: clamp(2rem, 6vw, 5rem) 1.5rem 4rem;
}

/* Wide enough for a two-line title to break where the copy wants it; the subtitle keeps the
   narrower reading measure of its own. */
.mkt-hero {
    max-width: var(--mkt-hero-width, 820px);
}

.mkt-hero h1 {
    margin: 0 0 1.25rem;
    color: var(--text-hi);
    font-size: clamp(2.25rem, 6vw, 3.5rem);
    line-height: 1.06;
    font-weight: 700;
}

.mkt-hero-subtitle {
    margin: 0;
    max-width: 620px;
    font-size: var(--text-lg);
    line-height: 1.6;
}

.mkt-section {
    margin-top: clamp(3.5rem, 8vw, 6rem);
}

/* For a section that follows the hero directly, with no card row or rule between them. */
.mkt-section--tight {
    margin-top: clamp(3rem, 7vw, 5rem);
}

.mkt-section-title {
    margin: 0 0 2rem;
    color: var(--text-hi);
    font-size: clamp(1.6rem, 4vw, 2.25rem);
    line-height: 1.15;
    font-weight: 700;
}

/* The numbered loop. The gap is shared with TkMarketingRailStep, whose rail segments overflow by
   exactly this amount at both ends so they join across it. Changing it here changes the rail. */
.mkt-steps {
    --mkt-step-gap: 4.5rem;

    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--mkt-step-gap);
}

/* Fact cards */

.mkt-cards {
    display: grid;
    gap: 1.25rem;
}

/* Three cards go straight from one column to three: a two-column step would strand the third
   card alone in a half-empty row. */
@media (min-width: 900px) {
    .mkt-cards--three {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

.mkt-card {
    border: 1px solid var(--border);
    border-radius: var(--tk-radius-lg);
    background: var(--surface-canvas);
    padding: 1.5rem;
}

.mkt-card h3 {
    margin: 0 0 0.625rem;
    color: var(--text-hi);
    font-size: 1.2rem;
    font-weight: 700;
}

.mkt-card p {
    margin: 0;
    line-height: 1.6;
}

/* Cross-links. Deliberately unboxed: these are footnotes, and as a bordered card they
   out-weighted the CTA directly beneath them. */

.mkt-links {
    margin-top: clamp(2.5rem, 6vw, 4rem);
    /* Matches .mkt-links + .mkt-cta's margin so the links centre between the two rules. */
    padding-top: 2.5rem;
    border-top: 1px solid var(--border);
}

.mkt-links p {
    margin: 0 0 0.625rem;
    line-height: 1.6;
}

.mkt-links p:last-child {
    margin-bottom: 0;
}

.mkt-links a {
    color: var(--brand);
    text-decoration: none;
}

.mkt-links a:hover {
    color: var(--text-hi);
}

/* Closing CTA. No surface of its own: one rule and generous space set it apart, which is as
   much as it needs. It is already the widest block on the page, centred, and holding the only
   saturated element in the layout — a panel behind it only competes with the button it exists
   to present. Nothing closes it at the bottom; the footer does that. */
.mkt-cta {
    margin-top: clamp(3rem, 7vw, 4.5rem);
    padding: clamp(2.5rem, 6vw, 4rem) 0;
    border-top: 1px solid var(--border);
    text-align: center;
}

/* Where the CTA follows the cross-links, its rule closes the band the links' rule opened, so
   the two gaps have to match for the links to sit centred in it. Both sides are named here to
   keep them changing together. */
.mkt-links + .mkt-cta {
    margin-top: 2.5rem;
}

.mkt-cta .mkt-section-title {
    margin-bottom: 1.75rem;
}

/* The button component stretches to its container; this keeps it button-shaped and centred. */
.mkt-cta-button {
    display: inline-block;
    min-width: 15rem;
}

/* ─── Marketing pages: phone ───
   Two things change. Prose pins to a hard 1rem: the app raises --font-scale to 1.125 on
   phones so feed reading stays comfortable, but marketing prose is set at a display measure
   and that bump makes it shout. The landing page pins the same way, and these pages are read
   next to it. Everything else here is rhythm — the desktop section gaps are tuned for a wide
   page and read as dead space in a 350px column. Headings are already smaller than the
   landing page's and stay as they are. */
@media (max-width: 767px) {
    .mkt-main {
        /* The landing page's phone gutter. */
        padding: 1.5rem 1.25rem 3rem;
    }

    /* These are documentation pages, not the landing page: the hero is a label for what
       follows, not the thing itself, so it drops well under the landing hero's size. */
    .mkt-hero h1 {
        font-size: 1.7rem;
        line-height: 1.2;
    }

    .mkt-section-title {
        font-size: 1.35rem;
    }

    .mkt-hero-subtitle {
        font-size: 1rem;
        line-height: 1.7;
    }

    .mkt-section {
        margin-top: 2.75rem;
    }

    .mkt-section--tight {
        margin-top: 2.5rem;
    }

    /* Also pulls the rail segments in, which overflow by exactly this amount to join. */
    .mkt-steps {
        --mkt-step-gap: 3rem;
    }

    .mkt-card p,
    .mkt-links p {
        font-size: 1rem;
    }

    .mkt-links {
        margin-top: 2.5rem;
        padding-top: 2rem;
    }

    /* Inline, the link wrapped mid-phrase: the lead ran to the end of a line, the link
       started there and broke, leaving an L-shaped tap target and no way to scan question
       from answer. As a block it starts on its own line and stays one target. */
    .mkt-links p {
        margin-bottom: 1.5rem;
    }

    /* Flex rather than a plain block so a label that wraps hangs indented beside the arrow
       instead of running back under it. */
    .mkt-links a {
        display: flex;
        align-items: baseline;
        gap: 0.4rem;
        margin-top: 0.25rem;
        font-weight: 600;
    }

    .mkt-links a::before {
        content: "\2192";
        flex: 0 0 auto;
    }

    .mkt-cta {
        margin-top: 2.75rem;
        padding: 2.5rem 0;
    }

    /* Restated at this breakpoint: the sibling selector outranks .mkt-cta above, so the
       phone pair has to be set here too or the band goes lopsided again. */
    .mkt-links + .mkt-cta {
        margin-top: 2rem;
    }

    /* Full-width for the thumb, like the landing hero's CTA. */
    .mkt-cta-button {
        display: block;
        min-width: 0;
    }
}

/* Inverted fill — shared pattern for primary/active states.
   Specificity uses div.web-app (0,2,1) to beat scoped CSS (0,2,0). */
div.web-app .tk-fill-inverted {
    background: var(--text-hi);
    color: var(--surface-app);
    font-weight: 600;
}
div.web-app .tk-fill-inverted:hover {
    opacity: 0.85;
    background: var(--text-hi);
}

/* Billing screens */
.tk-billing-screen {
    display: flex;
    min-height: 100%;
    align-items: center;
    justify-content: center;
    overflow-y: auto;
    padding: 2rem 1rem;
    background: var(--surface-app);
}

.tk-billing-panel {
    width: min(100%, 34rem);
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    padding: 1.5rem;
    border: 1px solid var(--border);
    border-radius: var(--tk-radius-xl);
    box-shadow: 0 1.25rem 3rem rgba(var(--black-rgb), 0.28);
}

.tk-billing-panel--plain {
    background: var(--surface-canvas);
}

.tk-billing-copy {
    display: grid;
    gap: 0.65rem;
}

.tk-billing-copy h1 {
    margin: 0;
    font-family: var(--font-display);
    font-size: var(--text-4xl);
    line-height: 1.05;
}

.tk-billing-copy p,
.tk-billing-actions p {
    margin: 0;
    color: var(--text-mid);
    font-size: var(--text-base);
    line-height: 1.55;
}

.tk-billing-actions {
    display: grid;
    gap: 0.75rem;
}

@media (max-width: 520px) {
    .tk-billing-panel {
        padding: 1rem;
    }

    /* The desktop display size overwhelms a phone-width panel once the headline wraps. */
    .tk-billing-copy h1 {
        font-size: var(--text-3xl);
    }
}


/* ─── Wizard Entrance Animation ─── */

@keyframes tk-wizard-fade-up {
    from {
        opacity: 0;
        transform: translateY(20px);
    }
    to {
        opacity: 1;
        transform: none;
    }
}

@keyframes tk-wizard-fade-in {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

/* Single-element entrance */
.tk-wizard-animate {
    animation: tk-wizard-fade-up 0.6s ease-out both;
}

.tk-wizard-animate--delay-1 { animation-delay: 0.15s; }
.tk-wizard-animate--delay-2 { animation-delay: 0.3s;  animation-duration: 0.5s; }
.tk-wizard-animate--delay-3 { animation-delay: 0.4s;  animation-duration: 0.5s; }

/* Auto-staggered children — add this class to any container
   and direct children animate in sequence. */
.tk-wizard-stagger > * {
    animation: tk-wizard-fade-up 0.45s ease-out both;
}

.tk-wizard-stagger > :nth-child(2) { animation-delay: 0.08s; }
.tk-wizard-stagger > :nth-child(3) { animation-delay: 0.16s; }
.tk-wizard-stagger > :nth-child(4) { animation-delay: 0.24s; }
.tk-wizard-stagger > :nth-child(5) { animation-delay: 0.32s; }
.tk-wizard-stagger > :nth-child(6) { animation-delay: 0.40s; }
.tk-wizard-stagger > :nth-child(n+7) { animation-delay: 0.46s; }

/* Vertical transform animations inflate scrollable overflow while they run.
   Wizard shells are scroll containers, so they use opacity-only entrance motion. */
.tk-feed-wizard-shell .tk-wizard-animate,
.tk-feed-wizard-shell .tk-wizard-stagger > * {
    animation-name: tk-wizard-fade-in;
}

/* Authenticated layout — main content area */
.tk-layout-main {
    background-color: var(--surface-app);
}

@media (pointer: fine) {
    .tk-layout-main {
        scrollbar-gutter: stable both-edges;
    }
}

/* The feed reads as a single centered column: the header, banners, and the article
   list all cap to the user's feed-width preference and center. 'full' resolves to
   100%, restoring the edge-to-edge layout. Used by FeedPage and by TkLayoutShell's
   alerts when the layout opts in. */
.tk-feed-column {
    max-width: var(--feed-max-width);
    margin-inline: auto;
}

.tk-layout-main-alerts {
    z-index: 30;
    display: grid;
    gap: 0.75rem;
    padding: 1rem 2rem 0;
    background: linear-gradient(
        to bottom,
        color-mix(in srgb, var(--surface-app) 98%, transparent) 0%,
        color-mix(in srgb, var(--surface-app) 92%, transparent) 72%,
        transparent 100%);
}

@media (max-width: 760px) {
    .tk-layout-main-alerts {
        padding: 0.75rem 1rem 0;
    }
}

/* Authenticated layout — mobile tab bar */
.tk-mobile-tab-bar {
    background-color: color-mix(in srgb, var(--surface-app) 95%, transparent);
}

@media (max-width: 767px) {
    :root {
        --tk-snackbar-bottom: calc(4rem + 0.75rem + env(safe-area-inset-bottom));
    }
}

.tk-mobile-tab-bar--auto-hide {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 30;
    transition: transform 220ms cubic-bezier(0.32, 0.72, 0, 1);
}

.tk-mobile-tab-bar--auto-hide.tk-mobile-tab-bar--stowed {
    transform: translateY(100%);
    transition-duration: 420ms;
}

@media (prefers-reduced-motion: reduce) {
    .tk-mobile-tab-bar--auto-hide {
        transition: none;
    }
}

/* Dropdown primitives — shared by TkDropdownPicker and TkMultiSelectFilter */
.tk-dropdown-anchor {
    position: relative;
}

.tk-dropdown-backdrop {
    position: fixed;
    inset: 0;
    z-index: 40;
}

.tk-dropdown-panel {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 41;
    min-width: 180px;
    max-height: 240px;
    overflow-y: auto;
    padding: 4px;
    background: var(--surface-canvas);
    border: 1px solid var(--border);
    border-radius: var(--tk-radius-lg);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.04);
    animation: tk-dropdown-in 0.15s ease-out;
}

.tk-dropdown-option {
    display: block;
    width: 100%;
    padding: 0.45rem 0.75rem;
    border: none;
    background: none;
    color: var(--text-mid);
    font-family: inherit;
    font-size: var(--text-sm);
    text-align: left;
    border-radius: var(--tk-radius-sm);
    cursor: pointer;
}

@keyframes tk-dropdown-in {
    from {
        opacity: 0;
        transform: translateY(-4px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Filter primitives — shared by TkMultiSelectFilter and TkSingleSelectFilter */

/* Trigger surface — sits next to a search input, so it mirrors the input surface
   and stretches to match its height (the row uses items-stretch). */
.tk-filter-trigger {
    position: relative;
    display: inline-flex;
    align-items: center;
    height: 100%;
    background: var(--surface-input);
    border: 1px solid var(--border-input);
    border-radius: var(--tk-radius-lg);
    color: var(--text-mid);
    font-family: inherit;
    font-size: var(--text-sm);
    font-weight: 500;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s;
}

.tk-filter-trigger:hover {
    border-color: var(--border-hover);
    color: var(--text-hi);
}

/* Icon-only variant, sized to the height of the search field it sits beside so the pair reads as one
   control. Without the width it collapses to its icon and hangs off the row as a squished tab. */
.tk-filter-trigger--square {
    justify-content: center;
    width: 3.05rem;
    padding: 0;
}

/* Corner dot marking a non-default choice. Pinned as an overlay so toggling a filter never changes
   the trigger's width, which would shift the search field beside it. */
.tk-filter-trigger-dot {
    position: absolute;
    top: -0.25rem;
    right: -0.25rem;
    width: 0.6rem;
    height: 0.6rem;
    background: var(--brand);
    border: 2px solid var(--surface-app);
    border-radius: var(--tk-radius-full, 9999px);
}

/* The list the phone sheet stacks its options in; each row is a TkCheckOption. */
.tk-filter-sheet-options {
    display: flex;
    flex-direction: column;
    padding-bottom: 0.5rem;
}

/* ─── Modal entrance — shared by dialog overlays and sheets ─── */

.tk-modal-overlay-in {
    animation: tk-modal-overlay-in 0.3s ease-out;
}

.tk-modal-card-in {
    animation: tk-modal-card-in 0.45s cubic-bezier(0.2, 0.8, 0.2, 1);
}

@keyframes tk-modal-overlay-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes tk-modal-card-in {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

/* Anything anchored to the bottom edge comes up off it rather than fading in place: a sheet that
   appears where it already is has no bottom edge to have come from. Used by TkMobileSheet and by
   TkConfirmDialog once it takes the sheet shape on a phone. */
@keyframes tk-modal-sheet-in {
    from {
        transform: translateY(100%);
    }

    to {
        transform: translateY(0);
    }
}

/* The micro label a flow dialog wears in its head: it names the flow rather than the screen, so it
   stays a brand-accented caption above the step instead of a heading competing with it. */
.tk-modal-head {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 1.375rem 1.375rem 0.375rem;
}

.tk-modal-head-title {
    display: inline-flex;
    align-items: center;
    gap: 0.4375rem;
    font-size: var(--text-xs);
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--brand);
}

/* A list row that is itself the control on a phone, opening the sheet that carries its options.
   :active only — hover latches on touch, leaving the last-tapped row lit after the sheet closes. */
.tk-tap-row {
    cursor: pointer;
    transition: background 0.15s;
}

.tk-tap-row:active {
    background: var(--surface-hover);
}

/* Past the first step the back control replaces the head: the label already says what leaving the
   step does, so one line of chrome keeps the options in view. */
.tk-modal-back {
    margin-bottom: 0.875rem;
}

.tk-modal-back .tk-back-link {
    font-size: var(--text-xs);
}

/* Each step of a multi-step dialog arrives rather than replaces: the screen redrawing in place
   reads as one form mutating, the lift reads as having moved forward. */
.tk-modal-step {
    animation: tk-modal-step-in 0.22s ease;
}

@keyframes tk-modal-step-in {
    from {
        opacity: 0;
        transform: translateY(0.375rem);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

/* Sidebar account footer */
.tk-user-footer {
    position: relative;
    border-top: 1px solid var(--border);
}

.tk-user-footer--borderless {
    border-top: none;
}

/* Collapse the footer entirely when it has no content, so the separator never
   floats alone (e.g. the settings sidebar, which shows no trial card or menu). */
.tk-user-footer:not(:has(> *)) {
    display: none;
}

.tk-user-btn {
    color: var(--text-mid);
    background: transparent;
    border: none;
    cursor: pointer;
    font-size: var(--text-base);
}

.tk-user-avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 50%;
    background: rgba(var(--white-rgb), 0.08);
    color: var(--text-mid);
    font-size: var(--text-xs);
    font-weight: 600;
    flex-shrink: 0;
    line-height: 1;
}

.tk-user-email {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-align: left;
}

/* ── Feed tuning sheet: chrome shared across the step components ── */
.tk-tune-prompt {
    margin: 0 0 0.875rem;
    font-size: calc(1.0625rem * var(--font-scale));
    font-weight: 650;
    color: var(--text-hi);
}

/* ── Coverage editor frame: shared by the Defaults and Per topic surfaces, which are separate
   components rendering the same shell ── */

/* The same measure Topics reads at. The wizard shell caps every step at max-w-5xl (64rem), but the
   subject picker applies that cap and its px-12 gutter a second time inside the shell, so Topics
   actually sits on 52rem. Coverage took the shell's full 64rem and ran noticeably wider than the
   steps either side of it. Below the breakpoint the viewport is narrower than this anyway, so it
   only ever bites on desktop. */
.tk-coverage-editor {
    max-width: 52rem;
    margin-inline: auto;
}

.tk-coverage-editor-panel {
    margin-top: 1.25rem;
    min-height: 12rem;
}

@media (max-width: 767px) {
    .tk-coverage-editor-panel {
        margin-top: 0.9rem;
    }
}

/* The quiet brand-coloured Reset / Clear text action offered by the coverage list surfaces: the Per
   topic filter options, that sheet's title row on a phone, and the no-matches state. Size and weight
   come from whatever it sits on, so one rule covers all three. */
.tk-coverage-reset-action {
    padding: 0;
    border: none;
    background: transparent;
    font: inherit;
    color: var(--brand);
    cursor: pointer;
    transition: color 0.15s;
}

.tk-coverage-reset-action:hover {
    color: var(--brand-hover);
}

/* ─── Menu / dropdown surface ─── */
/* Shared visual language for floating menus and dropdowns: a canvas panel of rows.
   Consumers (TkUserMenu, …) own
   only their trigger, positioning and any extra chrome; the panel, headers, rows,
   check and divider come from here so every menu reads the same. Rows opt into the
   quiet hover with the `tk-hover-surface` utility. */
.tk-menu {
    padding: 0.375rem;
    background: var(--surface-canvas);
    border: 1px solid var(--border);
    border-radius: var(--tk-radius-lg);
    box-shadow: 0 8px 24px rgba(var(--black-rgb), 0.4);
}

.tk-menu-section {
    display: flex;
    flex-direction: column;
}

.tk-menu-header {
    padding: 0.375rem 0.625rem 0.25rem;
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--text-lo);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.tk-menu-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    width: 100%;
    padding: 0.45rem 0.625rem;
    border: none;
    border-radius: var(--tk-radius-md);
    background: transparent;
    color: var(--text-mid);
    font-family: inherit;
    font-size: var(--text-caption);
    text-decoration: none;
    text-align: left;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}

.tk-menu-item.active {
    color: var(--text-hi);
}

.tk-menu-item:disabled {
    color: var(--text-faint);
    cursor: default;
}

.tk-menu-item:disabled:hover {
    background: transparent;
}

.tk-menu-check {
    width: 0.875rem;
    height: 0.875rem;
    flex-shrink: 0;
    color: var(--brand);
}

.tk-menu-divider {
    height: 1px;
    margin: 0.25rem 0.375rem;
    background: var(--border);
}

/* Settings preferences tab content — shared by the Account and Reading tab components. */
.tk-settings-preferences-content {
    display: flex;
    flex-direction: column;
    gap: 1.75rem;
}

.tk-settings-label {
    display: block;
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--text-hi);
    margin-bottom: 0.75rem;
}

.tk-settings-hint {
    font-size: var(--text-base);
    line-height: 1.5;
    color: var(--text-mid);
    margin: 0;
}

/* ═══════════════════════════════════════════════════════
   Feed setup — mobile bottom bar

   Shared by the create, edit and onboarding bars: same surface, same back control, same section
   pill. Only the trailing action differs (Next while building a feed, Done once it exists), so it
   stays with each bar. App-level rather than scoped because several components render it.
═══════════════════════════════════════════════════════ */

.tk-feed-setup-mobile-bar {
    position: sticky;
    bottom: 0;
    z-index: 30;
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 0.5rem;
    padding: 0.625rem 0.875rem calc(0.625rem + env(safe-area-inset-bottom));
    background: color-mix(in srgb, var(--surface-app) 95%, transparent);
    backdrop-filter: blur(0.5rem);
    -webkit-backdrop-filter: blur(0.5rem);
    border-top: 1px solid var(--border);
}

@media (min-width: 768px) {
    .tk-feed-setup-mobile-bar {
        display: none;
    }
}

.tk-feed-setup-mobile-bar-side {
    display: flex;
    justify-content: flex-start;
    min-width: 0;
}

.tk-feed-setup-mobile-bar-side--end {
    justify-content: flex-end;
}

.tk-feed-setup-mobile-bar-back .tk-icon-btn {
    border-radius: var(--tk-radius-full);
    background: var(--surface-input);
}

.tk-feed-setup-mobile-bar-section {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    max-width: 100%;
    min-width: 0;
    padding: 0.5rem 0.875rem;
    border: 1px solid var(--border);
    border-radius: var(--tk-radius-full);
    background: var(--surface-input);
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--text-hi);
    cursor: pointer;
}

.tk-feed-setup-mobile-bar-section-icon {
    display: inline-flex;
    color: var(--text-mid);
}

/* Breadcrumb context: the space stays quiet so the section the user is on reads as the
   current location. The chevron is decorative, so it lives in CSS rather than the markup. */
.tk-feed-setup-mobile-bar-section-space {
    flex-shrink: 0;
    white-space: nowrap;
    color: var(--text-mid);
}

.tk-feed-setup-mobile-bar-section-space::after {
    content: "\203A";
    margin-left: 0.375rem;
    color: var(--text-lo);
}

.tk-feed-setup-mobile-bar-section-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tk-feed-setup-mobile-bar-section-caret {
    display: inline-flex;
    color: var(--text-lo);
}

/* Edit's middle pill is a location readout, not a control: the home screen carries the navigation,
   so the pill keeps naming where the user stands without promising a menu behind a tap. */
.tk-feed-setup-mobile-bar-section--static {
    cursor: default;
}

/* What the onboarding subjects step is waiting for, in the slot its preview pill will take: quiet
   guidance, not a control, capped so it wraps between back and Next instead of running under them. */
.tk-feed-setup-mobile-bar-hint {
    min-width: 0;
    max-width: 12.5rem;
    margin: 0;
    font-size: var(--text-xs);
    line-height: 1.35;
    text-align: center;
    color: var(--text-mid);
}

/* Onboarding's bar carries no middle section pill (one space, strictly linear run): two columns
   keep the trailing action pinned to the right edge. */
.tk-feed-setup-mobile-bar--onboarding {
    grid-template-columns: 1fr auto;
}

.tk-feed-setup-mobile-bar--context .tk-feed-setup-mobile-bar-side {
    min-width: auto;
}

/* Edit's trailing action: the feed already exists, so leaving is a plain confirmation, not a step. */
.tk-feed-setup-mobile-bar-done {
    padding: 0.5rem 0.25rem;
    border: none;
    background: none;
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--brand);
    cursor: pointer;
}


/* The home screen of a feed flow (edit and create): the page column its title, identity card, and
   grouped step lists stack in. Narrow on purpose — it is a menu, not a work surface. */
.tk-feed-flow-overview {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    width: 100%;
    max-width: 36rem;
    margin: 0 auto;
    padding-top: 0.5rem;
}

.tk-feed-flow-overview-title {
    margin: 0;
    color: var(--text-hi);
    font-size: var(--text-3xl);
    font-weight: 600;
    line-height: 1.2;
}


/* The identity card a hub screen leads with (the settings overview's account, the edit home's feed):
   a glyph tile, a name over a hint naming what is behind the tap, and a chevron. Components add
   their own glyph shape on top of the shared shell. */
.tk-hub-card {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    padding: 0.85rem;
    background: var(--tk-surface-subtle);
    border: 1px solid var(--border);
    border-radius: var(--tk-radius-lg);
    text-decoration: none;
    transition: background 0.15s;
}

/* Pointer only, and :active for the tap — hover latches on touch (see TkCoverageThemeRow). */
@media (hover: hover) {
    .tk-hub-card:hover {
        background: var(--surface-hover);
    }
}

.tk-hub-card:active {
    background: var(--surface-hover);
}

.tk-hub-card-glyph {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 2.75rem;
    height: 2.75rem;
    background: var(--surface-input);
    color: var(--brand);
}

.tk-hub-card-identity {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-width: 0;
    gap: 0.1rem;
}

.tk-hub-card-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text-hi);
    font-size: var(--text-base);
    font-weight: 600;
}

.tk-hub-card-hint {
    color: var(--text-mid);
    font-size: var(--text-sm);
}

.tk-hub-card-chevron {
    display: inline-flex;
    flex-shrink: 0;
    color: var(--text-lo);
}


/* ─── Subject hub topic card ───
   The row a subject hub lists another topic as, shared by the public hub's related/catalog rails
   (TkSubjectHubSubjectCard) and the in-feed hub's related rail (TkFeedTopicSubjectRow). Same shell,
   different actions: the public one drafts a guest's feed, the in-feed one follows into a real one.
   The actions sit over the row's right edge rather than in its flow, so a narrow rail spends its
   width on the topic's name and only borrows it back while the row is being acted on. */

.tk-subject-hub-card-shell {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.tk-subject-hub-card {
    position: relative;
    background: linear-gradient(var(--surface-input), var(--surface-input)), var(--surface-canvas);
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem;
    overflow: hidden;
    border: 1px solid transparent;
    border-radius: var(--tk-radius-lg);
    transition: border-color 0.15s ease;
}

.tk-subject-hub-card-main {
    display: flex;
    flex: 1 1 auto;
    align-items: center;
    gap: 0.75rem;
    min-width: 0;
}

.tk-subject-hub-card-actions {
    position: absolute;
    inset-block: 0;
    right: 0;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    justify-content: center;
    gap: 0.3rem;
    padding: 0 0.5rem;
    background: var(--surface-canvas);
    transition: opacity 0.15s ease;
}

/* Feathers the actions' own background into the name they cover, so the overlay reads as the row
   dimming under them rather than as a panel dropped on top. */
.tk-subject-hub-card-actions::before {
    content: "";
    position: absolute;
    inset-block: 0;
    right: 100%;
    width: 2rem;
    background: inherit;
    -webkit-mask-image: linear-gradient(to right, transparent, #000);
    mask-image: linear-gradient(to right, transparent, #000);
}

/* Opt-in: the row stays quiet until it is pointed at. Left off where the actions are the only way to
   reach them — a touch surface has no hover to reveal them with. */
.tk-subject-hub-card--reveal .tk-subject-hub-card-actions {
    opacity: 0;
    pointer-events: none;
}

.tk-subject-hub-card--open,
.tk-subject-hub-card:active {
    border-color: color-mix(in srgb, var(--border) 70%, transparent);
}

@media (hover: hover) {
    .tk-subject-hub-card:hover {
        border-color: color-mix(in srgb, var(--border) 70%, transparent);
    }

    .tk-subject-hub-card--reveal:hover .tk-subject-hub-card-actions,
    .tk-subject-hub-card--reveal:has(:focus-visible) .tk-subject-hub-card-actions {
        opacity: 1;
        pointer-events: auto;
    }

    .tk-subject-hub-card-actions .tk-action-btn--quiet:hover,
    .tk-subject-hub-card-actions .tk-action-btn--quiet:focus-visible {
        color: var(--brand);
        border-color: transparent;
    }
}

.tk-subject-hub-card-shell--compact {
    width: 100%;
}

.tk-subject-hub-card--compact {
    gap: 0.6rem;
    padding: 0.4rem;
    background: linear-gradient(var(--surface-input), var(--surface-input)), var(--surface-app);
}

.tk-subject-hub-card--compact .tk-subject-hub-card-actions {
    background: var(--surface-app);
}

@media (prefers-reduced-motion: reduce) {
    .tk-subject-hub-card,
    .tk-subject-hub-card-actions {
        transition: none;
    }
}

.tk-subject-hub-latest {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    min-width: 0;
}

.tk-subject-hub-latest-title {
    flex-shrink: 0;
    margin: 0;
    color: var(--text-hi);
    font-size: var(--text-sm);
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}


.tk-subject-hub-coverage-row {
    color: var(--text-mid);
    transition: color 0.15s;
}

.tk-subject-hub-coverage-row--selected {
    color: var(--text-hi);
    font-weight: 600;
}

.tk-subject-hub-coverage-count {
    color: var(--text-faint);
    font-size: var(--text-sm);
    font-weight: 400;
}

.tk-subject-hub-coverage-row:hover {
    color: var(--text-hi);
}

.tk-subject-hub-coverage-row:hover .tk-check-toggle:not(.tk-check-toggle--active) {
    border-color: var(--text-lo);
}

/* ═══════════════════════════════════════════════════════
   PRODUCT SHOT MAT
   ═══════════════════════════════════════════════════════ */

/* The passe-partout a marketing screenshot is presented in, shared by the marketing rail
   steps and the landing page. It is what lifts a shot off the page: TkLandingShot's own
   border is deliberately quiet, which reads as flat when several shots stack down a dark
   section. */
.tk-shot-mat {
    padding: 0.6rem;
    border: 1px solid rgba(var(--text-hi-rgb), 0.14);
    border-radius: calc(var(--tk-radius-lg) + 0.35rem);
    background: rgba(var(--text-hi-rgb), 0.05);
    box-shadow: 0 28px 70px rgba(0, 0, 0, 0.55);
}

/* Portrait captures (a dialog, a phone) would otherwise tower over the copy beside them. */
.tk-shot-mat .tk-shot-portrait {
    max-height: 560px;
    width: auto;
    margin: 0 auto;
}
