/* Outfit, self-hosted.

   Served from our own origin, never from fonts.gstatic.com: loading a webfont
   from Google's CDN transmits the visitor's IP address to Google, which German
   courts have held requires consent (LG München I, 3 O 17493/20). A heading
   face is not worth a consent gate - least of all on the landing page, whose
   whole job is to not lose the visitor - and self-hosting removes the question
   entirely. See docs/design/landing_page.md §5.

   Two files, both variable across the whole 100-900 weight axis, so every
   weight the design uses costs one download rather than one per weight. The
   unicode-range split is Google's own: a visitor who never renders an accented
   character never fetches the latin-ext file. Licensed under the SIL Open Font
   License 1.1 - app/assets/fonts/OFL.txt travels with the font files, which is
   what that license asks for.

   Outfit has no CJK coverage, so anything Chinese falls through to the next
   family in the stack whatever this says. It is a face for headings, not for
   the characters the site teaches. */
@font-face {
    font-family: 'Outfit';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url("/assets/outfit-latin-ext-e51c1c3c.woff2") format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Outfit';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url("/assets/outfit-latin-c0a8d111.woff2") format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
    --bs-primary: #5942c1;
    --bs-primary-rgb: 89, 66, 193;
    --bs-link-color: var(--bs-primary);
    --bs-link-hover-color: #5b58e6;
    --bs-pagination-active-bg: #ab96d3;

    --primary: var(--bs-primary);
    --gray-100: #f8f9fa;
    --gray-200: #e9ecef;
    --gray-300: #dee2e6;
    --gray-400: #ced4da;
    --gray-500: #adb5bd;
    --gray-600: #6c757d;
    --gray-700: #495057;
    --gray-800: #343a40;
    --gray-900: #212529;
    --gray-850: #2a2d2f;
    --border-width: 1px;
    --border-color: var(--gray-300);
    --border-radius: 0.375rem;
}

.btn-primary {
    --bs-btn-color: #fff !important;
    --bs-btn-hover-color: #fff !important;
    --bs-btn-active-color: #fff !important;
    --bs-btn-bg: var(--bs-primary) !important;
    --bs-btn-border-color: var(--bs-primary) !important;
    --bs-btn-hover-bg: var(--bs-link-hover-color) !important;
    --bs-btn-hover-border-color: var(--bs-link-hover-color) !important;
    --bs-btn-active-bg: var(--bs-link-hover-color) !important;
    --bs-btn-active-border-color: var(--bs-link-hover-color) !important;
    --bs-btn-disabled-bg: var(--bs-primary) !important;
    --bs-btn-disabled-border-color: var(--bs-primary) !important;
}

.btn-outline-primary {
    --bs-btn-color: var(--bs-primary) !important;
    --bs-btn-border-color: var(--bs-primary) !important;
    --bs-btn-hover-bg: var(--bs-primary) !important;
    --bs-btn-hover-border-color: var(--bs-primary) !important;
    --bs-btn-hover-color: #fff !important;
    --bs-btn-active-bg: var(--bs-primary) !important;
    --bs-btn-active-border-color: var(--bs-primary) !important;
    --bs-btn-active-color: #fff !important;
}

.progress-bar {
    background-color: var(--bs-primary) !important;
}

.border-top {
    border-top: var(--border-width) solid var(--border-color);
}

.border-bottom {
    border-bottom: var(--border-width) solid var(--border-color);
}

.border-top-0 {
    border-top: 0 !important;
}

.border-bottom-0 {
    border-bottom: 0 !important;
}

.border-x {
    border-left: var(--border-width) solid var(--border-color);
    border-right: var(--border-width) solid var(--border-color);
}

.border-y {
    border-top: var(--border-width) solid var(--border-color);
    border-bottom: var(--border-width) solid var(--border-color);
}

.rounded-top {
    border-top-left-radius: var(--border-radius);
    border-top-right-radius: var(--border-radius);
}

.rounded-bottom {
    border-bottom-left-radius: var(--border-radius);
    border-bottom-right-radius: var(--border-radius);
}

.rounded-top-0 {
    border-top-left-radius: 0 !important;
    border-top-right-radius: 0 !important;
}

.rounded-bottom-0 {
    border-bottom-left-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
}

@media (min-width: 576px) {
    .border-sm-top {
        border-top: var(--border-width) solid var(--border-color);
    }

    .border-sm-bottom {
        border-bottom: var(--border-width) solid var(--border-color);
    }

    .border-sm-top-0 {
        border-top: 0 !important;
    }

    .border-sm-bottom-0 {
        border-bottom: 0 !important;
    }

    .border-sm-x {
        border-left: var(--border-width) solid var(--border-color);
        border-right: var(--border-width) solid var(--border-color);
    }

    .border-sm-y {
        border-top: var(--border-width) solid var(--border-color);
        border-bottom: var(--border-width) solid var(--border-color);
    }

    .rounded-sm-top {
        border-top-left-radius: var(--border-radius);
        border-top-right-radius: var(--border-radius);
    }

    .rounded-sm-bottom {
        border-bottom-left-radius: var(--border-radius);
        border-bottom-right-radius: var(--border-radius);
    }

    .rounded-sm-top-0 {
        border-top-left-radius: 0 !important;
        border-top-right-radius: 0 !important;
    }

    .rounded-sm-bottom-0 {
        border-bottom-left-radius: 0 !important;
        border-bottom-right-radius: 0 !important;
    }
}

@media (min-width: 768px) {
    .border-md-top {
        border-top: var(--border-width) solid var(--border-color);
    }

    .border-md-bottom {
        border-bottom: var(--border-width) solid var(--border-color);
    }

    .border-md-top-0 {
        border-top: 0 !important;
    }

    .border-md-bottom-0 {
        border-bottom: 0 !important;
    }

    .border-md-x {
        border-left: var(--border-width) solid var(--border-color);
        border-right: var(--border-width) solid var(--border-color);
    }

    .border-md-y {
        border-top: var(--border-width) solid var(--border-color);
        border-bottom: var(--border-width) solid var(--border-color);
    }

    .rounded-md-top {
        border-top-left-radius: var(--border-radius);
        border-top-right-radius: var(--border-radius);
    }

    .rounded-md-bottom {
        border-bottom-left-radius: var(--border-radius);
        border-bottom-right-radius: var(--border-radius);
    }

    .rounded-md-top-0 {
        border-top-left-radius: 0 !important;
        border-top-right-radius: 0 !important;
    }

    .rounded-md-bottom-0 {
        border-bottom-left-radius: 0 !important;
        border-bottom-right-radius: 0 !important;
    }
}

@media (min-width: 992px) {
    .border-lg-top {
        border-top: var(--border-width) solid var(--border-color);
    }

    .border-lg-bottom {
        border-bottom: var(--border-width) solid var(--border-color);
    }

    .border-lg-top-0 {
        border-top: 0 !important;
    }

    .border-lg-bottom-0 {
        border-bottom: 0 !important;
    }

    .border-lg-x {
        border-left: var(--border-width) solid var(--border-color);
        border-right: var(--border-width) solid var(--border-color);
    }

    .border-lg-y {
        border-top: var(--border-width) solid var(--border-color);
        border-bottom: var(--border-width) solid var(--border-color);
    }

    .rounded-lg-top {
        border-top-left-radius: var(--border-radius);
        border-top-right-radius: var(--border-radius);
    }

    .rounded-lg-bottom {
        border-bottom-left-radius: var(--border-radius);
        border-bottom-right-radius: var(--border-radius);
    }

    .rounded-lg-top-0 {
        border-top-left-radius: 0 !important;
        border-top-right-radius: 0 !important;
    }

    .rounded-lg-bottom-0 {
        border-bottom-left-radius: 0 !important;
        border-bottom-right-radius: 0 !important;
    }
}

@media (min-width: 1200px) {
    .border-xl-top {
        border-top: var(--border-width) solid var(--border-color);
    }

    .border-xl-bottom {
        border-bottom: var(--border-width) solid var(--border-color);
    }

    .border-xl-top-0 {
        border-top: 0 !important;
    }

    .border-xl-bottom-0 {
        border-bottom: 0 !important;
    }

    .border-xl-x {
        border-left: var(--border-width) solid var(--border-color);
        border-right: var(--border-width) solid var(--border-color);
    }

    .border-xl-y {
        border-top: var(--border-width) solid var(--border-color);
        border-bottom: var(--border-width) solid var(--border-color);
    }

    .rounded-xl-top {
        border-top-left-radius: var(--border-radius);
        border-top-right-radius: var(--border-radius);
    }

    .rounded-xl-bottom {
        border-bottom-left-radius: var(--border-radius);
        border-bottom-right-radius: var(--border-radius);
    }

    .rounded-xl-top-0 {
        border-top-left-radius: 0 !important;
        border-top-right-radius: 0 !important;
    }

    .rounded-xl-bottom-0 {
        border-bottom-left-radius: 0 !important;
        border-bottom-right-radius: 0 !important;
    }
}

@media (min-width: 1400px) {
    .border-xxl-top {
        border-top: var(--border-width) solid var(--border-color);
    }

    .border-xxl-bottom {
        border-bottom: var(--border-width) solid var(--border-color);
    }

    .border-xxl-top-0 {
        border-top: 0 !important;
    }

    .border-xxl-bottom-0 {
        border-bottom: 0 !important;
    }

    .border-xxl-x {
        border-left: var(--border-width) solid var(--border-color);
        border-right: var(--border-width) solid var(--border-color);
    }

    .border-xxl-y {
        border-top: var(--border-width) solid var(--border-color);
        border-bottom: var(--border-width) solid var(--border-color);
    }

    .rounded-xxl-top {
        border-top-left-radius: var(--border-radius);
        border-top-right-radius: var(--border-radius);
    }

    .rounded-xxl-bottom {
        border-bottom-left-radius: var(--border-radius);
        border-bottom-right-radius: var(--border-radius);
    }

    .rounded-xxl-top-0 {
        border-top-left-radius: 0 !important;
        border-top-right-radius: 0 !important;
    }

    .rounded-xxl-bottom-0 {
        border-bottom-left-radius: 0 !important;
        border-bottom-right-radius: 0 !important;
    }
}

a:not(.btn):not(.nav-link):not(.page-link),
a:not(.btn):not(.nav-link):not(.page-link):link,
a:not(.btn):not(.nav-link):not(.page-link):visited,
a:not(.btn):not(.nav-link):not(.page-link):active {
    color: var(--bs-primary);
    text-decoration: none;
}

a:not(.btn):not(.nav-link):not(.page-link):hover {
    color: var(--bs-link-hover-color);
    text-decoration: underline;
}

.color1,
.color1:hover {
    color: red !important;
}

.color2,
.color2:hover {
    color: #d80 !important;
}

.color3,
.color3:hover {
    color: green !important;
}

.color4,
.color4:hover {
    color: blue !important;
}

.color5,
.color5:hover {
    color: gray !important;
}

.uncolored,
a.uncolored,
a:link.uncolored,
a:visited.uncolored,
a:hover.uncolored,
a:active.uncolored {
    color: unset !important;
}

.bg-gray-100 {
    background-color: var(--gray-100);
}

.bg-gray-200 {
    background-color: var(--gray-200);
}

.bg-gray-300 {
    background-color: var(--gray-300);
}

.bg-gray-400 {
    background-color: var(--gray-400);
}

.bg-gray-500 {
    background-color: var(--gray-500);
}

.bg-gray-600 {
    background-color: var(--gray-600);
}

.bg-gray-700 {
    background-color: var(--gray-700);
}

.bg-gray-800 {
    background-color: var(--gray-800);
}

.bg-gray-850 {
    background-color: var(--gray-850);
}

.bg-gray-900 {
    background-color: var(--gray-900);
}

.border-gray-800 {
    border-color: var(--gray-800) !important;
}

.border-black {
    border-color: #000000 !important;
}

.bg-tile {
    background-image: url("/assets/bg_tile-0229beb0.png");
}

a.external:after {
    color: var(--primary);
    padding-left: 2px;
    content: "🛪";
}

.pagination {
    --bs-pagination-active-bg: var(--bs-pagination-active-bg);
    --bs-pagination-active-border-color: var(--bs-pagination-active-bg);
    --bs-pagination-active-color: var(--bs-primary);
    --bs-pagination-color: var(--bs-primary);
    --bs-pagination-hover-color: var(--bs-link-hover-color);
}



/* Place all the styles related to the hanzi tree here. */

.hanzi_tree {
    text-align: center;
    overflow-x: auto;
    padding: 20px 0;
    width: 100%;
    display: block;
}

.hanzi_tree ul {
    display: inline-flex;
    flex-direction: row;
    justify-content: center;
    align-items: flex-start;
    padding-top: 24px;
    position: relative;
    margin: 0;
    padding-left: 0;
}

.hanzi_tree>ul {
    padding-top: 0;
}

/* Downward line from parent */
.hanzi_tree ul::before {
    content: '';
    position: absolute;
    top: 0;
    left: 50%;
    border-left: 2px solid var(--gray-500);
    width: 0;
    height: 24px;
}

.hanzi_tree>ul::before {
    display: none;
}

.hanzi_tree li {
    display: flex;
    flex-direction: column;
    align-items: center;
    position: relative;
    padding: 24px 4px 0px 4px;
    list-style-type: none;
    margin: 0;
}

/* Horizontal connectors between sibling nodes */
.hanzi_tree li::before,
.hanzi_tree li::after {
    content: '';
    position: absolute;
    top: 0;
    right: 50%;
    border-top: 2px solid var(--gray-500);
    width: 50%;
    height: 24px;
    box-sizing: border-box;
}

.hanzi_tree li::after {
    right: auto;
    left: 50%;
}

/* Vertical line going up from child center */
.hanzi_tree li::before {
    border-right: 2px solid var(--gray-500);
}

/* Remove connectors for single children */
.hanzi_tree li:only-child::after,
.hanzi_tree li:only-child::before {
    display: none;
}

.hanzi_tree li:only-child {
    padding-top: 0;
}

/* Remove horizontal line to the left of first child */
.hanzi_tree li:first-child::before {
    border-top: 0 none;
}

/* Remove horizontal line to the right of last child */
.hanzi_tree li:last-child::after {
    border-top: 0 none;
}

/* Inside element styling */
.hanzi_tree li span {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
    background: transparent;
    transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
    z-index: 10;
    padding: 2px 4px;
}

.hanzi_tree li span:hover {
    /* transform: scale(1.15); */
}

.hanzi_tree li span a {
    color: var(--bs-primary);
    text-decoration: none;
    /* font-weight: bold; */
    transition: color 0.2s ease-in-out;
}

.hanzi_tree li span a:hover {
    text-decoration: none;
    color: var(--bs-link-hover-color, #4338ca);
}

/* Alternative decomposition intermediate node */
.hanzi_tree .alt-node {
    background: linear-gradient(135deg, var(--gray-100) 0%, var(--gray-200) 100%);
    border: 1px dashed var(--bs-primary) !important;
    border-radius: 16px;
    padding: 2px 10px;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--bs-primary);
    box-shadow: none;
    min-width: unset;
    height: auto;
    display: inline-block;
    text-align: center;
    z-index: 10;
}

.hanzi_tree .alt-node.canon {
    background: linear-gradient(135deg, #e8e5f8 0%, #dcd7f5 100%);
    border: 1px solid var(--bs-primary) !important;
    color: var(--bs-primary);
}

.hanzi_tree .alt-node.alt-circle {
    width: 14px;
    height: 14px;
    min-width: 14px;
    padding: 0;
    border-radius: 50% !important;
    background: var(--bs-primary);
    border: 2px solid var(--bs-body-bg) !important;
    box-shadow: 0 0 0 1px var(--bs-primary);
    z-index: 10;
    display: inline-flex;
    margin: 4px 0;
}

.hanzi_tree .tree-dec-buttons {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    z-index: 20;
    position: relative;
}

.hanzi_tree .btn-dec {
    background: var(--bs-body-bg);
    border: 1px dashed var(--bs-primary);
    border-radius: 16px;
    padding: 2px 8px;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--bs-primary);
    cursor: pointer;
    transition: all 0.2s ease-in-out;
    outline: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.hanzi_tree .btn-dec:hover {
    background: var(--gray-100);
    border-color: var(--bs-primary);
}

.hanzi_tree .btn-dec.active {
    background: var(--bs-primary);
    border: 1px solid var(--bs-primary);
    color: #fff;
}

.hanzi_tree ul.progress-vertical-ul {
    display: inline-flex !important;
    flex-direction: column !important;
    align-items: center !important;
    padding-top: 0 !important;
    padding-left: 0 !important;
    position: relative;
    margin: 0;
}

.hanzi_tree ul.progress-vertical-ul::before {
    display: none !important;
}

.hanzi_tree li.marilyn-column {
    padding-left: 0 !important;
    padding-right: 0 !important;
}

.hanzi_tree ul.progress-vertical-ul>li {
    padding: 16px 0 0 0 !important;
    display: flex !important;
    flex-direction: column !important;
    align-items: center !important;
    position: relative;
    list-style-type: none;
    margin: 0;
}

.hanzi_tree ul.progress-vertical-ul>li::before {
    display: none !important;
}

.hanzi_tree ul.progress-vertical-ul>li::after {
    content: '' !important;
    display: block !important;
    position: absolute !important;
    top: 0 !important;
    right: 50% !important;
    border-right: 2px solid var(--gray-500) !important;
    height: 16px !important;
    width: 0 !important;
    z-index: 1;
}


.spinner-sm {
    width: 20px;
    height: 20px;
}

.spinner-image {
    max-width: 100%;
    height: auto;
}

.text-truncate-4 {
    display: -webkit-box;
    -webkit-line-clamp: 4;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* AI Image Cards and Grid */
.ai-image-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 1.5rem;
    margin-bottom: 2rem;
}

.ai-image-card {
    transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
    border: 1px solid var(--gray-200);
    overflow: hidden;
    background: var(--bs-body-bg);
}

.ai-image-card:hover {
    /* transform: translateY(-4px); */
    box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.08) !important;
}

.ai-image-card .card-img-wrapper {
    position: relative;
    width: 100%;
    padding-top: 100%;
    /* 1:1 Aspect Ratio */
    background-color: var(--gray-100);
    overflow: hidden;
}

.ai-image-card .card-img-wrapper img,
.ai-image-card .card-img-wrapper .placeholder-spinner {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.ai-image-batch-badge {
    position: absolute;
    top: 0.75rem;
    left: 0.75rem;
    background: rgba(255, 193, 7, 0.9);
    /* yellow warning */
    backdrop-filter: blur(4px);
    color: #000;
    font-size: 0.75rem;
    padding: 0.25rem 0.5rem;
    border-radius: 20px;
    font-weight: 500;
}

@keyframes pulse {
    0% {
        box-shadow: 0 0 0 0 rgba(89, 66, 193, 0.4);
    }

    70% {
        box-shadow: 0 0 0 10px rgba(89, 66, 193, 0);
    }

    100% {
        box-shadow: 0 0 0 0 rgba(89, 66, 193, 0);
    }
}

.pulsing-border {
    animation: pulse 2s infinite;
}

@keyframes confetti-fall {
    0% {
        transform: translateY(-100vh) rotate(0deg);
        opacity: 1;
    }

    100% {
        transform: translateY(100vh) rotate(720deg);
        opacity: 0;
    }
}

.confetti-piece {
    position: fixed;
    width: 10px;
    height: 10px;
    top: -10px;
    z-index: 9999;
    animation: confetti-fall 3s ease-in-out forwards;
}

.hover-scale {
    transition: transform 0.2s ease-in-out;
}

.hover-scale:hover {
    transform: scale(1.05);
}

/* Linear Timeline Styles */
.sticky-timeline {
    position: sticky;
    top: 54px;
    z-index: 1020;
}

.no-scrollbar::-webkit-scrollbar {
    display: none;
}

.no-scrollbar {
    -ms-overflow-style: none;
    scrollbar-width: none;
}

@keyframes pulse-timeline {
    0% {
        box-shadow: 0 0 0 0 rgba(89, 66, 193, 0.4);
    }

    70% {
        box-shadow: 0 0 0 8px rgba(89, 66, 193, 0);
    }

    100% {
        box-shadow: 0 0 0 0 rgba(89, 66, 193, 0);
    }
}

/* Linear Timeline Responsiveness */
.timeline-wrapper {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.25rem 0;
    transition: max-height 0.3s ease-in-out;
}

.timeline-wrapper:not(.is-expanded) {
    flex-wrap: nowrap;
    overflow: hidden;
    max-height: 38px;
}

.timeline-wrapper:not(.is-expanded) .btn-success {
    display: none !important;
}

.timeline-wrapper.is-expanded {
    flex-wrap: wrap;
    max-height: 500px;
}

/* Learning-target cards: the three suggestions on the home page
   (static_pages/_landing_targets) and the single "make this your target" card
   on a character or comic page. These used to be an inline style plus a :css
   block inside the home page's partial, which only worked while the card lived
   on one page. */
.target-card {
    background: linear-gradient(135deg, var(--bs-body-bg) 0%, var(--bs-tertiary-bg) 100%);
    transition: transform 0.2s, box-shadow 0.2s;
}

.target-card:hover {
    transform: translateY(-5px);
    box-shadow: 0 .5rem 1.5rem rgba(0, 0, 0, .15) !important;
}

/* Decorative only, and knowingly so: this element holds a single Chinese
   character, and Outfit carries no CJK glyphs, so the browser falls through to
   the sans-serif behind it either way. Kept because the fallback is what the
   card has always rendered - and because deleting the rule would invite
   somebody to "restore" it as a real change later. */
.target-card-hanzi {
    font-family: 'Outfit', sans-serif;
}

/* A block of explanatory prose above a section: kept to a readable measure
   rather than the full container width. */
.lead-prose {
    max-width: 40rem;
}

/* ---------------------------------------------------------------------------
   Landing page (static_pages/_landing*)

   The page a visitor with no learning target sees at "/". The design and the
   reasoning behind it are in docs/design/landing_page.md; the
   rules that matter to this stylesheet:

   - Three accent colours carry one idea each, and the same three are used by
     the hero's prose, the annotations over the artwork and nothing else. They
     are declared once here so a change cannot land in one place only.
   - Sections are transparent and the cards inside them are white, rather than
     each section being a card: the layout already puts this page inside a
     container on a grey body, and banding every section reads as noise.
--------------------------------------------------------------------------- */
/* Outfit is the landing page's heading face and nothing else's. It is a
   display face: it earns its place on eight headings a stranger reads once,
   and would only slow down the pages a learner uses every day. The body text
   stays on the system stack, which costs nothing and matches the rest of the
   site. Every heading below is Latin-only, so the 32 KB latin subset covers
   the whole page. */
.landing-hero-title,
.landing-h2,
.landing-step-title,
.landing-tile-title,
.landing-price-title,
.landing-closer-title {
    font-family: 'Outfit', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    letter-spacing: -0.015em;
}

.landing {
    --landing-person: var(--bs-primary);
    --landing-place: #b45309;
    --landing-part: #2e7d32;
    --landing-tint: #f4f1fb;
    --landing-card-shadow: 0 .25rem 1rem rgba(33, 37, 41, .07);
}

/* Hero ---------------------------------------------------------------- */
.landing-hero {
    padding: 1rem 0 2.5rem;
}

.landing-hero-title {
    font-weight: 700;
    font-size: clamp(2rem, 1.2rem + 2.6vw, 3.25rem);
    line-height: 1.12;
    text-wrap: balance;
    margin-bottom: 1rem;
}

.landing-hero-lead {
    font-size: 1.15rem;
    line-height: 1.6;
    color: var(--gray-700);
    max-width: 34rem;
    margin-bottom: 1.5rem;
}

.landing-key-person {
    color: var(--landing-person);
}

.landing-key-place {
    color: var(--landing-place);
}

.landing-key-part {
    color: var(--landing-part);
}

.landing-hero-trust {
    display: flex;
    align-items: center;
    gap: .5rem;
    color: var(--gray-600);
    font-size: .9375rem;
    margin-bottom: 0;
}

.landing-tick {
    color: var(--landing-part);
    flex-shrink: 0;
}

/* Hero artwork and its annotations ------------------------------------- */
.landing-figure {
    position: relative;
    margin: 0 0 1rem;
}

.landing-figure-frame {
    border-radius: 1rem;
    overflow: hidden;
    box-shadow: 0 .75rem 2.5rem rgba(33, 37, 41, .18);
}

.landing-figure-img {
    display: block;
    width: 100%;
    height: auto;
}

.landing-annotations {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    margin: 1rem 0 0;
}

.landing-annotation {
    display: flex;
    align-items: baseline;
    gap: .5rem;
    font-size: .9375rem;
    line-height: 1.35;
}

.landing-annotation-dot {
    display: inline-block;
    width: .5625rem;
    height: .5625rem;
    border-radius: 50%;
    flex-shrink: 0;
    transform: translateY(-.0625rem);
}

.landing-annotation-dot--person {
    background: var(--landing-person);
}

.landing-annotation-dot--place {
    background: var(--landing-place);
}

.landing-annotation-dot--part {
    background: var(--landing-part);
}

/* Wide enough to lay the annotations over the artwork they point at. Below
   this they stay a plain legend under the image: at narrow widths the chips
   cover the drawing they are explaining. The offsets are hand-placed against
   app/assets/images/landing/de5_comic.webp and have to be redone if that
   image is ever replaced. */
@media (min-width: 992px) {
    .landing-annotations {
        position: absolute;
        inset: 0;
        display: block;
        margin: 0;
    }

    .landing-annotation {
        position: absolute;
        align-items: center;
        max-width: 62%;
        background: rgba(255, 255, 255, .95);
        border-radius: 2rem;
        padding: .3125rem .75rem;
        font-size: .8125rem;
        box-shadow: 0 .125rem .5rem rgba(0, 0, 0, .2);
    }

    .landing-annotation--person {
        top: 4%;
        left: 3%;
    }

    .landing-annotation--bai {
        top: 4%;
        right: 3%;
    }

    .landing-annotation--shao {
        top: 46%;
        left: 3%;
    }

    .landing-annotation--place {
        bottom: 4%;
        left: 50%;
        transform: translateX(-50%);
        max-width: 80%;
    }
}

/* 白 + 勺 = 的, under the artwork. */
.landing-equation {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: .625rem;
    background: var(--bs-body-bg, #fff);
    border: var(--border-width) solid var(--border-color);
    border-radius: .75rem;
    padding: .875rem 1.25rem;
    box-shadow: var(--landing-card-shadow);
}

.landing-equation-part {
    font-size: 1.75rem;
    font-weight: 600;
    color: var(--landing-part);
    line-height: 1;
}

.landing-equation-op {
    color: var(--gray-500);
}

.landing-equation-target {
    font-size: 2.25rem;
    font-weight: 700;
    color: var(--landing-person);
    line-height: 1;
}

.landing-equation-gloss {
    color: var(--gray-600);
    font-size: .9375rem;
}

/* Sections -------------------------------------------------------------- */
.landing-section {
    padding: 2.5rem 0;
}

.landing-section--tinted {
    background: var(--landing-tint);
    border-radius: 1rem;
    padding: 2.5rem 1.5rem;
    margin: 1rem 0;
}

.landing-section-head {
    text-align: center;
    max-width: 44rem;
    margin: 0 auto 2rem;
}

.landing-h2 {
    font-weight: 700;
    font-size: clamp(1.5rem, 1.1rem + 1.3vw, 2.125rem);
    margin-bottom: .75rem;
    text-wrap: balance;
}

.landing-section-sub,
.landing-body {
    color: var(--gray-700);
    line-height: 1.6;
    margin-bottom: 0;
}

.landing-body + .landing-body {
    margin-top: 1rem;
}

.landing-section-closer {
    text-align: center;
    color: var(--gray-700);
    margin: 2rem 0 0;
}

/* The three method steps ------------------------------------------------ */
.landing-step {
    height: 100%;
    background: var(--bs-body-bg, #fff);
    border-radius: 1rem;
    padding: 1.75rem;
    box-shadow: var(--landing-card-shadow);
}

.landing-step-head {
    display: flex;
    align-items: center;
    gap: .75rem;
    margin-bottom: .875rem;
}

.landing-step-number {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    flex-shrink: 0;
    border-radius: 50%;
    background: var(--bs-primary);
    color: #fff;
    font-weight: 700;
}

.landing-step-title {
    font-size: 1.1875rem;
    font-weight: 600;
    margin: 0;
}

.landing-step-body {
    color: var(--gray-700);
    line-height: 1.6;
    margin-bottom: 0;
}

/* Route preview --------------------------------------------------------- */
.landing-route-card {
    background: var(--bs-body-bg, #fff);
    border-radius: 1rem;
    padding: 1.75rem;
    box-shadow: var(--landing-card-shadow);
}

.landing-route-label {
    text-transform: uppercase;
    letter-spacing: .04em;
    font-size: .8125rem;
    font-weight: 600;
    color: var(--gray-600);
    margin-bottom: 1rem;
}

.landing-route-pills {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
}

/* Every pill in the real timeline is a link; here they illustrate one, and
   the only thing worth clicking on this page is the target picker. */
.landing-route-pill {
    cursor: default;
    pointer-events: none;
}

.landing-route-ellipsis {
    color: var(--gray-500);
    letter-spacing: .1em;
}

.landing-route-caption {
    color: var(--gray-700);
    font-size: .9375rem;
    line-height: 1.55;
    margin: 1rem 0 0;
}

.landing-route-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    margin: 1rem 0 0;
    font-size: .8125rem;
    color: var(--gray-600);
}

.landing-route-legend li {
    display: flex;
    align-items: center;
    gap: .375rem;
}

.landing-legend-swatch {
    display: inline-block;
    width: .625rem;
    height: .625rem;
    border-radius: 50%;
}

.landing-legend-swatch--done {
    background: var(--bs-success, #198754);
}

.landing-legend-swatch--active {
    background: var(--bs-primary);
}

.landing-legend-swatch--ahead {
    border: var(--border-width) solid var(--gray-400);
}

/* Depth tiles ----------------------------------------------------------- */
.landing-tile {
    height: 100%;
    background: var(--bs-body-bg, #fff);
    border-radius: 1rem;
    padding: 1.5rem;
    box-shadow: var(--landing-card-shadow);
}

.landing-tile-icon {
    color: var(--bs-primary);
    margin-bottom: .75rem;
}

.landing-tile-title {
    font-size: 1.0625rem;
    font-weight: 600;
    margin-bottom: .5rem;
}

.landing-tile-body {
    color: var(--gray-700);
    font-size: .9375rem;
    line-height: 1.55;
    margin-bottom: 0;
}

/* Pricing --------------------------------------------------------------- */
.landing-price {
    height: 100%;
    border: var(--border-width) solid var(--border-color);
    border-radius: 1rem;
    padding: 1.75rem;
    background: var(--bs-body-bg, #fff);
}

.landing-price--paid {
    border: 2px solid var(--bs-primary);
    background: var(--landing-tint);
}

.landing-price-title {
    font-weight: 700;
    font-size: 1.3125rem;
    margin-bottom: .75rem;
}

.landing-price-body {
    color: var(--gray-700);
    line-height: 1.6;
    margin-bottom: 0;
}

/* Closing band ---------------------------------------------------------- */
.landing-closer {
    text-align: center;
    border-radius: 1rem;
    padding: 3rem 1.5rem;
    margin: 1rem 0 2rem;
    background: linear-gradient(180deg, var(--bs-primary) 0%, #47339e 100%);
    color: #fff;
}

.landing-closer-title {
    font-weight: 700;
    font-size: clamp(1.5rem, 1.1rem + 1.4vw, 2.25rem);
    margin-bottom: .75rem;
    text-wrap: balance;
}

.landing-closer-sub {
    color: rgba(255, 255, 255, .85);
    margin-bottom: 1.5rem;
}

.landing-closer-btn {
    background: #fff;
    color: var(--bs-primary);
}

.landing-closer-btn:hover,
.landing-closer-btn:focus {
    background: var(--gray-100);
    color: var(--bs-link-hover-color);
}

/* Comic archive (/comics) ------------------------------------------------
   A grid of identically sized 100x100 tiles, one per comic, each a crop of
   that comic's last panel. The crop is done here rather than by Active
   Storage: the markup asks for the [180, 135] variant every panel blob
   already carries, and object-fit does the squaring, so no page view
   commissions a MiniMagick transform. The oversized source also keeps the
   tile sharp on a hidpi screen. See docs/technical-references/comics_archive.md. */
.comic-archive-title {
    font-weight: 600;
    letter-spacing: -0.01em;
    margin: 2rem 0 .75rem;
}

.comic-archive-lead {
    color: var(--gray-600);
    max-width: 40rem;
    margin-bottom: 2rem;
}

.comic-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, 100px);
    /* start, not center: auto-fill lays down as many 100px tracks as the row
       holds, so centring would only shift the whole grid by the leftover
       remainder and leave it a few pixels out of line with the heading. */
    justify-content: start;
    gap: .75rem;
    list-style: none;
    padding: 0;
    margin: 0 0 2rem;
}

.comic-tile {
    position: relative;
    display: block;
    width: 100px;
    height: 100px;
    overflow: hidden;
    border-radius: .5rem;
    background: var(--gray-100);
    /* The hairline ring is the load-bearing half: a good many panels are drawn on
       a white or near-white background, and without it those tiles dissolve into
       the page and the grid stops reading as a grid. */
    box-shadow: 0 0 0 1px rgba(33, 37, 41, .10), 0 1px 2px rgba(33, 37, 41, .10);
    transition: transform .18s ease, box-shadow .18s ease;
}

.comic-tile:hover,
.comic-tile:focus-visible {
    transform: translateY(-2px);
    box-shadow: 0 0 0 1px rgba(89, 66, 193, .35), 0 .5rem 1rem rgba(89, 66, 193, .28);
}

.comic-tile:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: 2px;
}

.comic-tile-image {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform .3s ease;
}

.comic-tile:hover .comic-tile-image {
    transform: scale(1.06);
}

/* The caption repeats what the link already announces, so it is aria-hidden and
   purely an invitation to click: hidden until the tile is hovered or focused. */
.comic-tile-label {
    position: absolute;
    inset: auto 0 0 0;
    padding: 1rem .25rem .3rem;
    background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, .75) 65%);
    color: #fff;
    font-size: .9375rem;
    line-height: 1.2;
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    opacity: 0;
    transform: translateY(100%);
    transition: opacity .18s ease, transform .18s ease;
}

.comic-tile:hover .comic-tile-label,
.comic-tile:focus-visible .comic-tile-label {
    opacity: 1;
    transform: none;
}

/* A tile whose panel image is missing keeps the footprint - dropping it would
   make the grid disagree with the paginator - and shows the caption instead. */
.comic-tile-fallback {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    padding: .25rem;
    border: 1px dashed var(--gray-400);
    border-radius: .5rem;
    color: var(--gray-600);
    font-size: 1.25rem;
    line-height: 1.1;
    text-align: center;
    overflow: hidden;
}

/* No hover to reveal the caption on a touch screen, so it stays up. */
@media (hover: none) {
    .comic-tile-label {
        opacity: 1;
        transform: none;
    }
}

@media (prefers-reduced-motion: reduce) {

    .comic-tile,
    .comic-tile-image,
    .comic-tile-label {
        transition: none;
    }

    .comic-tile:hover,
    .comic-tile:focus-visible {
        transform: none;
    }

    .comic-tile:hover .comic-tile-image {
        transform: none;
    }
}

/* Reviews feed: a card is passed when the visitor scrolls it up into the top
   quarter of the viewport (see scroll_pass_controller.js), which is confirmed by
   the checkmark popping in and the card itself dimming. */

.review-item {
    position: relative;
}

.review-pass-badge {
    position: absolute;
    /* Centred on the card's own height. The translate has to travel with the
       pop-in scale below, since one transform property carries both. */
    top: 50%;
    right: 0.5rem;
    z-index: 2;
    font-size: 1.5rem;
    line-height: 1;
    pointer-events: none;
    visibility: hidden;
    opacity: 0;
    transform: translateY(-50%) scale(0.5);
    transition: opacity 0.3s ease, transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), visibility 0s linear 0.3s;
}

.review-passed .review-pass-badge {
    visibility: visible;
    opacity: 1;
    transform: translateY(-50%) scale(1);
    transition: opacity 0.3s ease, transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.review-item-body {
    transition: opacity 0.35s ease;
}

.review-passed .review-item-body {
    opacity: 0.55;
}

/* Room below the last review so it can be scrolled up into the top quarter of
   the screen, which is what passes it, before the congratulations message.
   Must stay at or above (1 - PASS_AT_FRACTION) of a screen - see
   scroll_pass_controller.js - or the last card can never reach the line. */
.review-scroll-spacer {
    min-height: 75vh;
}

@media (prefers-reduced-motion: reduce) {

    .review-pass-badge,
    .review-item-body {
        transition: none;
    }
}
