/*
 * Cairn: the game's own styles, on top of the inherited Bootstrap/blog layout.
 *
 * Edited directly rather than compiled from LESS — the LESS build in front_dev
 * belongs to the blog and admin pages it came with, and the game does not need
 * a build step to change a colour.
 */

:root {
    --cairn-ink: #2b2b28;
    --cairn-stone: #7a7266;
    --cairn-parchment: #f6f2e9;
    --cairn-edge: #ded5c4;
    --cairn-warm: #8d6e63;
    --cairn-found: #2e7d32;

    /* The site's own voice, and the fallback for any period that has not been
       given a face. A quest page overrides these two inline with the period's
       typography — see App\Zygote::getPeriodFonts and public/css/fonts.css.
       Custom properties rather than a class per period, because the periods
       themselves are edition data and a clone must not need a stylesheet rule
       for each one it invents. */
    --cairn-font-display: "Playfair Display", Georgia, serif;
    --cairn-font-text: "Playfair Display", Georgia, serif;
}

/* ---- the page itself ----
 *
 * The engraved parchment sits in a fixed layer rather than in
 * `background-attachment: fixed`, which iOS Safari ignores outright, and under
 * a scrim: the artwork has a Big Ben in one corner and body text has to stay
 * readable when it lands on top of it. The colour goes on <html> because a
 * background on <body> would paint over its own z-index:-1 pseudo-layers.
 *
 * The file is a convention — public/images/background.webp — the same way the
 * favicons are. A clone for another country replaces the file and edits
 * nothing.
 */

html {
    background-color: var(--cairn-parchment);
}

/* Bootstrap paints --bs-body-bg here, and a background on <body> covers the
   very layers below. */
body {
    background-color: transparent;
}

body::before,
body::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
}

body::before {
    background: url("/images/background.webp") center center / cover no-repeat;
}

body::after {
    background: linear-gradient(180deg, rgba(246, 242, 233, .38), rgba(246, 242, 233, .86));
}

/* A surface that has to hold text over the artwork. */
.cairn-panel {
    background: rgba(246, 242, 233, .94);
    border: 1px solid var(--cairn-edge);
    border-radius: 6px;
    box-shadow: 0 14px 40px rgba(43, 43, 40, .12);
}

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

/* ---- the top bar ----
 *
 * One row at every width. It sticks because the home page gives its first
 * screen to a single large image, and scrolling past that should not mean
 * scrolling back up to reach the quest.
 */

:root {
    --cairn-bar-h: 4.25rem;
}

.cairn-topbar {
    position: sticky;
    top: 0;
    z-index: 900;
    background: rgba(246, 242, 233, .93);
    backdrop-filter: blur(6px);
    border-bottom: 1px solid var(--cairn-edge);
}

/* The blog LESS puts 40px on top of every .container; inside the bar that is
   a 40px hole above the logo. */
.cairn-topbar .container {
    margin-top: 0;
}

/* ---- the editor's strip ----
 * Two sticky bars both pinned to top:0 land on top of each other. The admin
 * strip is a convenience for one person; the site's own bar is not. */
.admin-menu-bar {
    position: static;
}

.admin-bar-inner {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.admin-bar-label {
    font-size: .875rem;
    white-space: nowrap;
}

.admin-bar-menu {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex: 1;
}

.admin-bar-menu nav {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem 1.25rem;
}

.admin-bar-menu a {
    color: #fff;
    text-decoration: none;
    font-size: .875rem;
    white-space: nowrap;
}

.admin-bar-menu a:hover {
    color: #fff;
    text-decoration: underline;
}

.admin-bar-who {
    color: rgba(255, 255, 255, .55);
    font-size: .75rem;
    white-space: nowrap;
}

.admin-bar-toggle {
    display: none;
    flex-direction: column;
    justify-content: center;
    gap: 4px;
    width: 2.25rem;
    height: 1.9rem;
    margin-left: auto;
    padding: 0 .45rem;
    background: none;
    border: 1px solid rgba(255, 255, 255, .35);
    border-radius: 3px;
}

.admin-bar-toggle span {
    display: block;
    height: 2px;
    background: #fff;
}

@media (min-width: 992px) {
    .admin-bar-menu.collapse:not(.show) {
        display: flex;
    }
}

@media (max-width: 991.98px) {
    .admin-bar-inner {
        flex-wrap: wrap;
    }

    .admin-bar-toggle {
        display: flex;
    }

    .admin-bar-menu.collapse.show,
    .admin-bar-menu.collapsing {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: .5rem;
        /* 100% basis, not just width: with the flex-basis of 0 it inherits, it
           fits beside the label and never wraps onto its own line. */
        flex: 1 1 100%;
        padding: .5rem 0 .25rem;
    }

    .admin-bar-menu nav {
        flex-direction: column;
        gap: .5rem;
    }
}

.cairn-topbar-inner {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding-top: .5rem;
    padding-bottom: .5rem;
    min-height: var(--cairn-bar-h);
}

.cairn-brand {
    display: flex;
    align-items: center;
    gap: .65rem;
    margin-right: auto;
    text-decoration: none;
    color: var(--cairn-ink);
}

.cairn-brand-mark {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    flex: none;
}

.cairn-brand-name {
    display: block;
    font-family: var(--cairn-font-display);
    font-size: 1.5rem;
    line-height: 1.05;
}

.cairn-brand-tagline {
    display: block;
    font-size: .7rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--cairn-stone);
}

.cairn-nav {
    display: flex;
    align-items: center;
    gap: 1.75rem;
    margin-left: auto;
}

.cairn-nav-links {
    display: flex;
    align-items: center;
    gap: 1.25rem;
}

.cairn-nav-links a,
.cairn-account a {
    color: var(--cairn-stone);
    text-decoration: none;
    white-space: nowrap;
}

.cairn-nav-links a {
    border-bottom: 2px solid transparent;
    padding-bottom: 2px;
}

.cairn-nav-links a:hover,
.cairn-account a:hover {
    color: var(--cairn-ink);
}

.cairn-nav-links a:hover {
    border-bottom-color: var(--cairn-edge);
}

.cairn-nav-strong {
    color: var(--cairn-ink);
    font-weight: 600;
}

.cairn-account {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.cairn-account-name {
    color: var(--cairn-ink);
    font-weight: 600;
}

.cairn-nav-toggle {
    display: none;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    width: 2.75rem;
    height: 2.5rem;
    padding: 0 .6rem;
    background: none;
    border: 1px solid var(--cairn-edge);
    border-radius: 4px;
}

.cairn-nav-toggle span {
    display: block;
    height: 2px;
    background: var(--cairn-ink);
}

@media (min-width: 992px) {
    /* Bootstrap's collapse hides it; above the fold-out width it is just a row. */
    .cairn-nav.collapse:not(.show) {
        display: flex;
    }
}

@media (max-width: 991.98px) {
    .cairn-topbar-inner {
        flex-wrap: wrap;
    }

    .cairn-nav-toggle {
        display: flex;
    }

    .cairn-nav.collapse.show,
    .cairn-nav.collapsing {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: .75rem;
        width: 100%;
        margin: .5rem 0 .75rem;
    }

    .cairn-nav-links {
        flex-direction: column;
        align-items: flex-start;
        gap: .5rem;
    }
}

/* ---- Bootstrap's blue ----
 *
 * The theme these pages came with is Bootstrap's default: #0d6efd buttons and
 * links, on a site whose entire palette is ink and parchment. Remapped here
 * rather than in app.css because that file is a build artifact of front_dev
 * LESS and `make css` would take the change with it.
 */

.btn-primary,
.btn-primary:disabled {
    background-color: var(--cairn-ink);
    border-color: var(--cairn-ink);
    color: var(--cairn-parchment);
}

.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active,
.btn-primary:not(:disabled):not(.disabled):active {
    background-color: #43423c;
    border-color: #43423c;
    color: #fff;
}

.btn-primary:focus,
.btn-outline-primary:focus,
.form-control:focus,
.form-select:focus,
.form-check-input:focus {
    box-shadow: 0 0 0 .2rem rgba(122, 114, 102, .3);
    border-color: var(--cairn-stone);
}

.btn-outline-primary {
    color: var(--cairn-ink);
    border-color: var(--cairn-edge);
}

.btn-outline-primary:hover,
.btn-outline-primary:active {
    background-color: var(--cairn-ink);
    border-color: var(--cairn-ink);
    color: var(--cairn-parchment);
}

.btn-secondary {
    background-color: var(--cairn-stone);
    border-color: var(--cairn-stone);
}

.btn-secondary:hover,
.btn-secondary:focus,
.btn-secondary:active {
    background-color: var(--cairn-ink);
    border-color: var(--cairn-ink);
}

.form-check-input:checked {
    background-color: var(--cairn-ink);
    border-color: var(--cairn-ink);
}

/* Links inside the content, which Bootstrap also paints blue. */
main.container a:not(.btn):not(.cairn-btn):not(.cairn-card):not(.auth-social-btn) {
    color: var(--cairn-warm);
}

main.container a:not(.btn):not(.cairn-btn):not(.cairn-card):not(.auth-social-btn):hover {
    color: var(--cairn-ink);
}

/* ---- the auth pages ----
 *
 * The card came from another project with its own identity: a navy-to-indigo
 * gradient header and a blue button, neither of which has been anywhere near
 * this site. Sign-in is often the second page somebody sees, so it cannot be
 * the one page that looks like somewhere else.
 */

.auth-card .card {
    background: var(--cairn-parchment);
    border: 1px solid var(--cairn-edge) !important;
    border-radius: 6px;
}

.auth-hero {
    background: var(--cairn-ink);
    color: var(--cairn-parchment);
    border-bottom: 3px solid var(--cairn-warm);
}

.auth-hero h1 {
    font-family: var(--cairn-font-display);
    font-weight: 400;
    font-size: 1.7rem;
}

.auth-hero p {
    color: rgba(246, 242, 233, .72);
}

.auth-body {
    background: var(--cairn-parchment);
}

.auth-body .form-control {
    background: #fff;
    border-color: var(--cairn-edge);
}

.auth-divider {
    color: var(--cairn-stone);
}

.auth-social-btn {
    border-color: var(--cairn-edge) !important;
    color: var(--cairn-ink) !important;
}

/* Apple is the one social button that carries its own colour - a dark fill
 * with light text, which is what Apple's own guidance allows. The rule above
 * was repainting it ink on ink and the word "Apple" vanished, so it needs the
 * pair set explicitly, and !important again to outrank it. */
.auth-social-btn.is-apple {
    background: var(--cairn-ink);
    border-color: var(--cairn-ink) !important;
    color: var(--cairn-parchment) !important;
}

.auth-social-btn.is-apple:hover {
    background: #000;
    border-color: #000 !important;
    color: var(--cairn-parchment) !important;
}

.auth-foot {
    color: var(--cairn-stone);
}

/* ---- buttons ----
 * Bootstrap's primary blue has no place on parchment. */

.cairn-btn {
    display: inline-block;
    font-family: var(--cairn-font-display);
    text-decoration: none;
    border: 1px solid transparent;
    border-radius: 4px;
    padding: .55rem 1.4rem;
    cursor: pointer;
}

.cairn-btn-play {
    background: var(--cairn-ink);
    color: var(--cairn-parchment);
    font-size: 1.15rem;
    padding: .85rem 2.2rem;
    box-shadow: 0 6px 18px rgba(43, 43, 40, .22);
}

.cairn-btn-play:hover {
    background: #43423c;
    color: #fff;
}

.cairn-btn-quiet {
    background: rgba(255, 255, 255, .65);
    border-color: var(--cairn-edge);
    color: var(--cairn-ink);
}

.cairn-btn-quiet:hover {
    border-color: var(--cairn-stone);
    color: var(--cairn-ink);
}

/* ---- the content surface ----
 *
 * The parchment has an engraving on it, and body text laid straight over the
 * ruled lines of Westminster is readable but tiring. So the page's content sits
 * on a wash of the same parchment: enough opacity to settle the text down,
 * little enough that the artwork still shows through and the page does not turn
 * into a white box on a picture.
 *
 * The home page is the exception — it is built as an open screen with its own
 * panels, and boxing the poster would undo it.
 */

main.container {
    background: rgba(246, 242, 233, .78);
    border: 1px solid var(--cairn-edge);
    border-radius: 6px;
    padding: 2rem;
    box-shadow: 0 10px 34px rgba(43, 43, 40, .07);
}

/* The flash message. Two of the four layouts emit it above their own `<main>`,
   where it needs a `.container` to line up with the page; the other two put it
   inside one already, and a container inside a container indents it away from
   everything it sits above. Zero the inner padding in that case.

   It carries no height of its own — that all belongs to the alert — so
   dismissing the message leaves nothing behind. */
.container .cairn-message {
    padding-left: 0;
    padding-right: 0;
}

/* The home page is built as an open screen with its own panels, and the auth
   pages are a card on an empty page — a frame around either is a box around a
   box. */
main.container:has(> .cairn-home),
main.container:has(> .auth-page) {
    background: none;
    border: 0;
    box-shadow: none;
    padding: 0;
}

@media (max-width: 575.98px) {
    /* A hair of parchment down each side, so the panel reads as a sheet laid on
       the page rather than as the page itself. */
    main.container {
        width: auto;
        margin-left: 3px;
        margin-right: 3px;
        padding: 1.25rem 1rem;
    }
}

/* The blog theme stretches every button to the full width of a phone. It makes
   "Send" look like a page section, and it is not what any of these forms want:
   the ones that really are full width say so with .w-100 in the markup. */
@media (max-width: 767.98px) {
    .btn {
        width: auto;
    }

    .btn.w-100 {
        width: 100% !important;
    }
}

/* ---- the footer ----
 * The blog theme made it a grey slab, which over parchment reads as a hole in
 * the page rather than as the end of it. */

.cairn-footer {
    margin-top: 3rem;
    padding: 2.5rem 0 2rem;
    border-top: 1px solid var(--cairn-edge);
    background: rgba(246, 242, 233, .78);
    color: var(--cairn-stone);
}

.cairn-footer a {
    color: var(--cairn-stone);
    text-decoration: none;
}

.cairn-footer a:hover {
    color: var(--cairn-ink);
    text-decoration: underline;
}

.cairn-footer-top {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 2rem 3rem;
}

.cairn-footer-brand {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    max-width: 26rem;
}

.cairn-footer-mark img {
    width: 64px;
    height: 64px;
    border-radius: 50%;
}

/* Outranks `.cairn-footer a`, which would otherwise grey the name out. */
.cairn-footer .cairn-footer-name {
    display: block;
    font-family: var(--cairn-font-display);
    font-size: 1.6rem;
    line-height: 1.1;
    color: var(--cairn-ink);
}

.cairn-footer-name:hover {
    text-decoration: none;
}

.cairn-footer-tagline {
    font-size: .75rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--cairn-stone);
    margin-top: .15rem;
}

.cairn-footer-losung {
    margin: .75rem 0 0;
    font-size: .95rem;
}

.cairn-footer-links {
    display: flex;
    gap: 3rem;
}

.cairn-footer-links div {
    display: flex;
    flex-direction: column;
    gap: .4rem;
}

.cairn-footer-bottom {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-top: 2rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--cairn-edge);
}

.cairn-footer-social {
    display: flex;
    align-items: center;
    gap: .35rem;
}

.cairn-footer-social a {
    display: inline-flex;
    padding: .35rem;
    opacity: .55;
}

.cairn-footer-social a:hover {
    opacity: 1;
}

/* ---- the site's own accounts ----
 *
 * _shared/social_links.htm, in the top bar and in the footer. Marks only: a row
 * of icons is read as "find us elsewhere" without a word of explanation, and
 * there is no room in a header for anything else.
 *
 * These use the same partial as the share buttons and so the same 24px paths,
 * but not their colour. A share button is a place you are sending something and
 * the brand colour is what makes it findable in a hurry; this is a quiet
 * pointer at the foot and the corner of somebody else's page, and thirteen
 * brand colours across a header is a toolbar from 2011. They take the ink of
 * the text around them and brighten on hover. */
.cairn-social {
    display: flex;
    align-items: center;
    gap: .15rem;
}

.cairn-social-link {
    display: inline-flex;
    padding: .35rem;
    color: inherit;
    opacity: .55;
    transition: opacity .15s;
}

.cairn-social-link:hover,
.cairn-social-link:focus-visible {
    opacity: 1;
}

.cairn-social-link .cairn-share-icon {
    width: 1.05rem;
    height: 1.05rem;
    fill: currentColor;
}

.cairn-social-link .cairn-share-icon-fa {
    font-size: 1rem;
    line-height: 1.05rem;
    color: inherit;
}

/* In the top bar it sits after the account block, with a rule between them so
   it does not read as one more account link. */
.cairn-social-header {
    margin-left: .5rem;
    padding-left: .65rem;
    border-left: 1px solid var(--cairn-edge);
}

.cairn-social-footer .cairn-share-icon,
.cairn-footer-social .cairn-share-icon-fa {
    width: 1.2rem;
    height: 1.2rem;
    font-size: 1.1rem;
}

@media (max-width: 991.98px) {
    /* Inside the fold-out menu the row has a whole line to itself, so it goes
       back to being a row rather than a tail on the account block. */
    .cairn-social-header {
        margin: .5rem 0 0;
        padding: .65rem 0 0;
        border-left: 0;
        border-top: 1px solid var(--cairn-edge);
    }
}

.cairn-footer-legal {
    font-size: .85rem;
    text-align: right;
}

@media (max-width: 767.98px) {
    /* Wrapped under the brand, the two columns were sized by their longest
       link and left a ragged gap down the right. Give them half the width
       each and they line up. */
    .cairn-footer-links {
        flex: 1 1 100%;
        gap: 1rem;
    }

    .cairn-footer-links div {
        flex: 1 1 0;
        min-width: 0;
    }

    .cairn-footer-legal {
        text-align: left;
    }
}

/* ---- the apps ----
 *
 * _shared/app_badges.htm, under the home page's first screen and in the footer.
 *
 * The badges are Google's and Apple's own art and may not be recoloured, given
 * a border or set on a background of ours, so this block styles everything
 * around them and nothing on them. They are sized by height alone — the widths
 * differ by a few percent and the two badges are drawn to sit at the same
 * cap-height, so matching heights is what makes the row look level. */

.cairn-apps {
    display: flex;
    flex-direction: column;
    gap: .6rem;
}

.cairn-apps-heading {
    font-family: var(--cairn-font-display);
    font-size: 1.1rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--cairn-stone);
    margin: 0;
}

.cairn-apps-line {
    margin: 0;
    max-width: 34rem;
}

.cairn-apps-badges {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .75rem;
}

.cairn-app-badge img {
    display: block;
    height: 3rem;
    width: auto;
}

/* Not a button, so it does not lift or tint: the only thing that may happen to
   a store badge is that it stops being ignored. */
.cairn-app-badge {
    opacity: .92;
    transition: opacity .15s;
}

.cairn-app-badge:hover,
.cairn-app-badge:focus-visible {
    opacity: 1;
}

/* On the home page it is a band of its own, under the explainer, on the days
   with nothing live — set on the same panel as the explainer above it so the
   two read as one block: what this is, and where to get it. With a quest live
   the badges are not here at all; see .cairn-apps-quest. */
.cairn-apps-home {
    align-items: center;
    text-align: center;
    background: rgba(246, 242, 233, .9);
    border: 1px solid var(--cairn-edge);
    border-radius: 6px;
    padding: 1.5rem;
    margin-bottom: 2rem;
}

.cairn-apps-home .cairn-apps-badges {
    justify-content: center;
    margin-top: .35rem;
}

.cairn-apps-home .cairn-app-badge img {
    height: 3.4rem;
}

/* Beside a live quest, in the pass-on block under the share row
   (Root/home.htm). No panel and no heading of its own: it is the second half
   of a block the share row has already opened, and a second bordered card
   inside the quest column would read as a different subject.

   The badge row is centred like the share buttons above it, so the two rows
   share a middle whichever way the surrounding column is aligned. */
.cairn-apps-quest {
    margin: 1.25rem 0 0;
}

.cairn-apps-quest .cairn-apps-line {
    /* Smaller than the quest's own prose: this is an aside to somebody who has
       just read the question, not another paragraph of it. */
    font-size: .95rem;
    color: var(--cairn-stone);
    max-width: none;
}

.cairn-apps-quest .cairn-apps-badges {
    justify-content: center;
}

.cairn-apps-quest .cairn-app-badge img {
    height: 2.75rem;
}

/* With no share row above it — an edition with sharing off, or a quest with
   nothing to pass on — the apps become the first thing in the block and have
   to draw the rule the share row would have drawn. */
.cairn-passon > .cairn-apps-quest:first-child {
    margin-top: 2rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--cairn-edge);
}

/* In the footer it is one more column of the top row, beside the links. */
.cairn-apps-footer .cairn-apps-heading {
    font-size: .75rem;
    letter-spacing: .14em;
}

.cairn-apps-footer .cairn-app-badge img {
    height: 2.5rem;
}

@media (max-width: 767.98px) {
    /* A phone is where a badge matters most, so it gets the full width and the
       two stores stack rather than shrinking to fit side by side. */
    .cairn-apps-footer {
        flex: 1 1 100%;
    }

    .cairn-app-badge img {
        height: 2.75rem;
    }
}

/* ---- the explainer: what this site is, under every state of the home page ---- */

.cairn-explainer {
    background: rgba(246, 242, 233, .9);
    border: 1px solid var(--cairn-edge);
    border-radius: 6px;
    padding: 1.5rem;
    margin-bottom: 2rem;
}

/* The site's name in readable text. Set at body weight rather than as a
   heading-sized slab: this band is a footnote to the quest above it, and a
   second large title on the page would compete with the one that matters. */
.cairn-explainer-title {
    font-family: var(--cairn-font-display);
    font-size: 1.1rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--cairn-stone);
    margin: 0 0 .75rem;
}

.cairn-explainer .lead {
    margin-bottom: 0;
}

.cairn-explainer-links {
    margin: 1.25rem 0 0;
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
    font-size: .95rem;
}

.cairn-steps {
    list-style: none;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    margin: 1rem 0 0;
}

.cairn-steps li {
    flex: 1 1 12rem;
    color: var(--cairn-stone);
}

.cairn-steps span {
    display: inline-block;
    width: 1.6rem;
    height: 1.6rem;
    line-height: 1.6rem;
    text-align: center;
    border-radius: 50%;
    background: var(--cairn-ink);
    color: #fff;
    margin-right: .5rem;
}

/* ---- the home page ----
 *
 * The first screen carries one image and the state picks it: the quest's own
 * illustration when there is something to play, the poster when there is not.
 * Everything else — the explainer, yesterday, the archive — starts below the
 * fold, because the button is what the visit is for.
 */

/* No 40px gap between the bar and a screen that is meant to fill the window. */
main.container:has(> .cairn-home) {
    margin-top: 0;
}

.cairn-home {
    /* What the purpose line above the hero costs in height. Declared here
       rather than measured because .cairn-screen sizes itself against the
       viewport and has to subtract it — see below. Two lines' worth, which is
       what the sentence wraps to on a phone; on a wide screen it is one line
       and the hero simply gets a little more room than it asked for. */
    --cairn-purpose-h: 3.75rem;
}

/* One quiet sentence naming what the site is, above the hero in every state.
   Set as a subtitle to the site, not as part of the quest under it. */
.cairn-home-purpose {
    margin: 0;
    padding: .75rem 0 .5rem;
    text-align: center;
    font-family: var(--cairn-font-display);
    font-size: 1rem;
    line-height: 1.4;
    color: var(--cairn-stone);
}

.cairn-screen {
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 2rem 0 2.5rem;
    /* Fills a laptop screen, and stops there: on a tall monitor an uncapped
       100vh leaves the quest marooned in the middle of an empty page. The
       purpose line comes out of the same budget: without subtracting it the
       hero still claims a full viewport and pushes its own play button below
       the fold, which is the one thing this screen may not do. */
    min-height: calc(100vh - var(--cairn-bar-h) - var(--cairn-purpose-h));
    min-height: min(calc(100svh - var(--cairn-bar-h) - var(--cairn-purpose-h)), 52rem);
}

/* A: live and unsolved. Picture on one side, the question and the button on
   the other, both centred on the fold. */
.cairn-screen.cairn-live {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
    gap: clamp(1.5rem, 4vw, 3.5rem);
    align-items: center;
    align-content: center;
}

.cairn-screen.cairn-live-nopic {
    display: flex;
    text-align: center;
}

.cairn-live-art {
    margin: 0;
}

.cairn-live-art img {
    width: 100%;
    max-height: 62vh;
    object-fit: cover;
    background: #fff;
    border: 1px solid var(--cairn-edge);
    border-radius: 4px;
    box-shadow: 0 14px 40px rgba(43, 43, 40, .18);
}

.cairn-live-title {
    font-family: var(--cairn-font-display);
    font-size: clamp(2rem, 4.2vw, 3rem);
    line-height: 1.1;
    margin: 0 0 .75rem;
}

.cairn-live-words .cairn-year {
    margin-bottom: 1rem;
}

.cairn-live-nopic .cairn-year {
    justify-content: center;
}

/* Three lines of scene-setting and no more: the story is the quest page's job,
   and a long paragraph here pushes the button off the screen. */
.cairn-live-words .cairn-scene {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.cairn-live-words .cairn-question {
    font-size: 1.25rem;
    font-weight: 600;
}

.cairn-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    margin-top: 1.25rem;
    color: var(--cairn-stone);
}

.cairn-meta .cairn-counter,
.cairn-meta .cairn-streak {
    margin-top: 0;
}

/* B: live and already solved.
 *
 * The same two-column shape as A, because it is the same day: the quest's own
 * picture and title on one side, the score on the other. The card used to be
 * the score alone and centred, which said how well they did and never which
 * quest it was for — a problem the moment there is a Share button under it. */
.cairn-screen.cairn-solved {
    align-items: center;
}

.cairn-result-card {
    padding: 0;
    text-align: left;
}

/* The picture is not the hero here. The player has already been looking at it
   for ten minutes; on this screen it is identification, not an invitation. */
.cairn-solved .cairn-live-art img {
    max-height: 42vh;
}

.cairn-solved-heading {
    font-family: var(--cairn-font-display);
    font-size: clamp(1.6rem, 3vw, 2.2rem);
    margin: 0 0 .75rem;
}

/* Under the heading the title is a caption for the picture rather than the
   billboard it is in state A. */
.cairn-solved .cairn-live-title {
    font-size: clamp(1.4rem, 2.6vw, 1.9rem);
}

/* The share row and the app badges under it. Set on the block rather than on
   the share row alone so the two halves stay aligned with each other. */
.cairn-solved .cairn-passon {
    /* A left-aligned column, so the centred button row needs its own middle
       rather than the panel's. */
    text-align: left;
}

.cairn-result-card h1 {
    font-family: var(--cairn-font-display);
}

/* The quest page has its own, smaller .cairn-score; this is the home page's
   result, where the number is the whole point of the card. */
.cairn-result-card .cairn-score {
    font-family: var(--cairn-font-display);
    font-size: 3rem;
    font-weight: 400;
    line-height: 1;
    color: var(--cairn-found);
    margin: .75rem 0 1.25rem;
}

.cairn-result-card .cairn-score span {
    display: block;
    margin-top: .5rem;
    font-family: var(--cairn-font-text);
    font-size: .8rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--cairn-stone);
}

.cairn-result-place {
    margin-top: 1.25rem;
}

/* C: nothing live. The poster is the page. */
.cairn-screen.cairn-poster-screen {
    align-items: center;
    text-align: center;
}

.cairn-poster {
    max-width: min(100%, 62rem);
    max-height: 58vh;
    width: auto;
    height: auto;
    border: 1px solid var(--cairn-edge);
    border-radius: 6px;
    box-shadow: 0 18px 50px rgba(43, 43, 40, .18);
}

.cairn-poster-foot {
    margin-top: 1.75rem;
}

/* The same poster below a live quest, where it is the second thing on the page
   rather than the whole of it: smaller, and given room to read as a break
   rather than as more quest. */
.cairn-intro {
    margin: 4.5rem 0 3rem;
    text-align: center;
}

/* Wide enough to be looked at rather than glanced past. Sized by width, not by
   viewport height — a height cap here would quietly undo the width on any short
   window, since the image keeps its ratio. */
.cairn-intro .cairn-poster {
    width: 80%;
    max-width: 80%;
    max-height: none;
}

.cairn-next-label {
    margin: 0;
    font-size: .8rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--cairn-stone);
}

.cairn-poster-foot .cairn-countdown {
    font-family: var(--cairn-font-display);
    font-size: 1.8rem;
    color: var(--cairn-ink);
    margin: .25rem 0 0;
}

.cairn-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    margin-top: 1.5rem;
}

/* Side by side, so they have to be the same height. */
.cairn-actions .cairn-btn-quiet {
    font-size: 1.15rem;
    padding: .85rem 1.6rem;
}

.cairn-poster-screen .cairn-actions,
/* The card is a left-aligned column now, so its buttons start where its text
   does. On a phone the whole screen centres and this follows it. */
.cairn-result-card .cairn-actions {
    justify-content: flex-start;
}

@media (max-width: 767.98px) {
    .cairn-result-card {
        text-align: center;
    }

    .cairn-result-card .cairn-actions {
        justify-content: center;
    }

    .cairn-solved .cairn-passon {
        text-align: center;
    }
}

.cairn-card-wide {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .75rem;
}

.cairn-card-wide strong,
.cairn-card-wide span {
    display: inline;
}

@media (max-width: 767.98px) {
    /* One column, and the picture gives up most of its height: on a phone the
       button matters more than the engraving. */
    .cairn-screen.cairn-live {
        display: flex;
        text-align: center;
    }

    .cairn-live-art img {
        max-height: 30vh;
    }

    .cairn-meta,
    .cairn-actions,
    .cairn-live-words .cairn-year {
        justify-content: center;
    }

    .cairn-poster {
        max-height: 44vh;
    }

    /* On a phone 80% is just a smaller picture with margins either side. */
    .cairn-intro .cairn-poster {
        width: 100%;
        max-width: 100%;
    }

    .cairn-btn-play {
        display: block;
        text-align: center;
    }
}

/* ---- the quest page's hero ---- */

.cairn-hero {
    text-align: center;
    padding: 2rem 0;
}

.cairn-hero-image {
    max-width: 100%;
    height: auto;
    border-radius: 4px;
    margin-bottom: 1rem;
}

.cairn-scene {
    font-family: var(--cairn-font-text);
    font-size: 1.2rem;
    font-style: italic;
    color: var(--cairn-stone);
}

/* The year. It never leaves the screen — it is the spine of the puzzle, the
   thing that turns a whole country into a shortlist — so it is set large and
   unmissable rather than as a caption. */
.cairn-year {
    display: flex;
    align-items: baseline;
    gap: .5rem;
    font-family: var(--cairn-font-display);
    font-size: 2rem;
    line-height: 1.1;
    letter-spacing: .04em;
    color: var(--cairn-found);
}

/* "793" on its own is a number nobody can place. */
.cairn-year-label {
    font-family: var(--cairn-font-text);
    font-size: .8rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--cairn-stone);
}

.cairn-question {
    font-size: 1.15rem;
    margin: 1rem 0;
}

.cairn-scene-block {
    margin-bottom: 1.5rem;
}

.cairn-scene-image {
    max-width: 100%;
    height: auto;
    border-radius: 4px;
    margin-bottom: 1rem;
}

.cairn-hint-preview {
    border-left: 3px solid var(--cairn-edge);
    padding-left: 1rem;
    margin: 1.5rem auto;
    max-width: 34rem;
    text-align: left;
    font-family: "Playfair Display", Georgia, serif;
}

.cairn-countdown {
    font-size: 1.4rem;
    color: var(--cairn-stone);
}

.cairn-counter,
.cairn-streak {
    color: var(--cairn-stone);
    margin-top: 1rem;
}

.cairn-section {
    margin: 3rem 0;
}

.cairn-card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
    gap: 1rem;
}

.cairn-card {
    display: block;
    border: 1px solid var(--cairn-edge);
    border-radius: 4px;
    padding: 1rem;
    text-decoration: none;
    color: inherit;
    background: rgba(255, 255, 255, .72);
}

.cairn-card:hover {
    border-color: var(--cairn-stone);
}

.cairn-card img {
    max-width: 100%;
    height: auto;
    border-radius: 3px;
    margin-bottom: .5rem;
}

.cairn-card strong,
.cairn-card span {
    display: block;
}

.cairn-played {
    color: var(--cairn-found);
    font-size: .9rem;
}

/* ---- your history ----
 *
 * The same card as the archive, with the answer added. It is a `div` rather
 * than an `a` because it has two destinations — the quest and the place behind
 * it — and a link inside a link is not a thing a browser will render. */
.cairn-history-card {
    display: flex;
    flex-direction: column;
    gap: .35rem;
}

.cairn-history-main {
    display: block;
    text-decoration: none;
    color: inherit;
}

.cairn-history-main:hover strong {
    text-decoration: underline;
}

.cairn-history-meta {
    font-size: .8rem;
}

/* The answer, which is the thing this list has that the archive does not. Set
   off by a rule above it rather than by a colour of its own: it takes the
   site's link colour, which is what it is, and that leaves green to mean "you
   found it" and nothing else on this card. */
.cairn-history-place {
    font-weight: 600;
    text-decoration: none;
    border-top: 1px solid var(--cairn-edge);
    padding-top: .35rem;
}

.cairn-history-place:hover {
    text-decoration: underline;
}

.cairn-history-result {
    font-size: .9rem;
    font-weight: 600;
}

.cairn-history-result.is-found {
    color: var(--cairn-found);
}

.cairn-history-date {
    font-size: .8rem;
}

/* ---- the quest page ---- */

/* Two rows. The year and the mode go together — they are both "when and where
   you are" — and the title gets the full width to itself, which is what makes
   fitting it to that width worth doing at all. Centred, because the rail is
   narrow and a ragged left edge under a centred year looks like a mistake. */
.cairn-quest-header {
    text-align: center;
    margin-bottom: 1rem;
}

.cairn-quest-meta {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .75rem;
    flex-wrap: wrap;
}

/* The size is set by public/js/questtitle.js, which measures the text against
   the width it has. What is declared here is everything that measurement needs
   to stay true: the ratio it multiplies to get a line, and the clamp that
   holds a title it could not fit to two lines. */
.cairn-quest-title {
    font-family: var(--cairn-font-display);
    margin: .35rem 0 0;
    /* Blackletter and uncial need room to breathe, and they run small for
       their point size next to a humanist face. */
    line-height: 1.22;
    font-size: 1.6rem;
    /* Nothing to gain from breaking a word in a title: the fitter would rather
       drop a size than hyphenate. */
    overflow-wrap: normal;
}

.cairn-quest-title.cairn-title-two-lines {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
    /* Two even lines rather than a long one and an orphan. Ignored where it is
       not supported, which costs nothing. */
    text-wrap: balance;
}

/* Which mode this quest is being played in. Not Bootstrap's badge: bg-success
   is a green from another palette, on a page whose only green is the one the
   game already uses to mean "found". */
.cairn-quest-mode {
    font-family: var(--cairn-font-text);
    font-size: .7rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    padding: .2rem .55rem;
    border-radius: 999px;
    white-space: nowrap;
}

.cairn-quest-mode-live {
    color: #fff;
    background: var(--cairn-found);
}

.cairn-quest-mode-archive {
    color: var(--cairn-stone);
    background: rgba(122, 114, 102, .14);
    border: 1px solid var(--cairn-edge);
}

.cairn-hint {
    border: 1px solid var(--cairn-edge);
    border-radius: 4px;
    padding: 1rem;
    margin-bottom: 1rem;
    background: var(--cairn-parchment);
}

.cairn-hint-locked {
    background: #fff;
    border-style: dashed;
}

/* The panel the last confirm turned to. The mark stays until the next confirm
   supersedes it — the effects announce its arrival, this is what a player finds
   when they come back to the rail a minute later still thinking. */
.cairn-hint-new {
    position: relative;
    border-color: var(--cairn-warm);
    box-shadow: 0 0 0 2px rgba(141, 110, 99, .18);
}

.cairn-hint-flag {
    position: absolute;
    top: -.6rem;
    right: .75rem;
    font-family: var(--cairn-font-text);
    font-size: .65rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: #fff;
    background: var(--cairn-warm);
    border-radius: 999px;
    padding: .15rem .5rem;
}

.cairn-hint-number {
    font-size: .75rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--cairn-stone);
    margin-bottom: .5rem;
}

.cairn-hint-image {
    max-width: 100%;
    height: auto;
    border-radius: 3px;
    margin-bottom: .75rem;
}

.cairn-hint-text {
    font-family: "Playfair Display", Georgia, serif;
    font-size: 1.1rem;
    margin: 0;
}

.cairn-hint-credit {
    margin: .5rem 0 0;
}

.cairn-first-sight {
    font-family: "Playfair Display", Georgia, serif;
    font-size: 1.1rem;
    color: var(--cairn-found);
}

.cairn-credits {
    list-style: none;
    padding: 0;
}

.cairn-checkpoint-help {
    margin-top: .5rem;
}

.cairn-scorebar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1.5rem;
    padding: .5rem 0;
    color: var(--cairn-stone);
}

/* How many confirms are left: the one number a player is working against, and
   for a long time the quietest thing on the screen. It is set as a counter now
   — ink, not stone; figures that do not reflow as they change — with the pips
   repeating it in a form that can be read without reading. */
.cairn-progress {
    display: flex;
    align-items: center;
    gap: .6rem;
    color: var(--cairn-ink);
}

/* Deliberately not in the period's display face. That face dresses the story —
   the year, the title, the scene — and this is chrome: a counter set in uncial
   is a counter somebody has to decipher. */
.cairn-progress-text {
    font-size: 1.05rem;
    font-weight: 700;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.cairn-pips {
    display: flex;
    gap: .3rem;
}

.cairn-pip {
    width: .55rem;
    height: .55rem;
    border-radius: 50%;
    border: 1px solid var(--cairn-stone);
    opacity: .45;
}

/* Spent. */
.cairn-pip-used {
    background: var(--cairn-ink);
    border-color: var(--cairn-ink);
    opacity: 1;
}

/* The one about to be made. It breathes, because this is the only thing on the
   map that is asking for an action. */
.cairn-pip-now {
    background: var(--cairn-found);
    border-color: var(--cairn-found);
    opacity: 1;
    animation: cairn-pip-pulse 1.8s ease-in-out infinite;
}

@keyframes cairn-pip-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(46, 125, 50, .55); }
    50% { box-shadow: 0 0 0 .28rem rgba(46, 125, 50, 0); }
}

/* Someone who has told their browser they do not want motion has told it about
   this too. */
@media (prefers-reduced-motion: reduce) {
    .cairn-pip-now {
        animation: none;
    }
}

/* ---- the playing frame ----
   Only on /quest/{id}, which uses _outer/quest.htm. Everything here exists to
   give the map the screen: the site's two rows of navigation and its footer are
   right for a page you read and wrong for the one screen where the game
   happens. */

.cairn-playing {
    --cairn-topbar: 3.25rem;
    margin: 0;
    /* A column that owns the viewport, with the stage taking whatever the bar
       and any flash message leave. Measuring instead — 100vh minus a hard-coded
       bar height — is right until something appears above the stage that the
       arithmetic never heard of, and then the map is a few pixels too tall and
       the page gains a scrollbar it should not have. */
    height: 100vh;
    /* On iOS Safari `100vh` is the viewport as it would be with the browser
       chrome retracted — and the chrome only retracts when a page is scrolled,
       which this one never is. So the bottom ~4rem of the layout sat under
       Safari's bottom bar for good, and that is exactly where the confirm
       button lives: reported as "could not tap confirm, the browser overlay
       was in the way". `svh` is the viewport with the chrome showing, which
       is the height the player actually has. Not `dvh`: it changes as the
       chrome moves and would resize Leaflet's container mid-play. */
    height: 100svh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.cairn-quest-main {
    flex: 1;
    /* Without this a flex child refuses to shrink below its content, which is
       how an overflow:auto pane ends up not scrolling. */
    min-height: 0;
}

.cairn-quest-main > .cairn-quest {
    height: 100%;
}

.cairn-slim-bar {
    /* The dropdown below hangs out of this box, and the map is a later sibling
       with panes of its own up at z-index 800. Without a stacking context here
       the menu opens underneath the map. */
    position: relative;
    z-index: 1000;
    display: flex;
    align-items: center;
    gap: 1.5rem;
    height: var(--cairn-topbar);
    padding: 0 1rem;
    border-bottom: 1px solid var(--cairn-edge);
    background: rgba(246, 242, 233, .96);
}

.cairn-slim-brand {
    font-family: "Playfair Display", Georgia, serif;
    font-size: 1.25rem;
    color: var(--cairn-ink);
    text-decoration: none;
    white-space: nowrap;
}

.cairn-slim-nav {
    display: flex;
    gap: 1.25rem;
    flex: 1;
    flex-wrap: wrap;
    overflow: hidden;
}

.cairn-slim-nav a {
    color: var(--cairn-stone);
    text-decoration: none;
    white-space: nowrap;
}

.cairn-slim-nav a:hover {
    color: var(--cairn-ink);
}

/* The toggle exists only below the fold-out width; above it the links are just
   a row and there is nothing to fold out. */
.cairn-slim-toggle {
    display: none;
    flex-direction: column;
    justify-content: center;
    gap: 4px;
    width: 2.25rem;
    height: 1.85rem;
    margin-left: auto;
    padding: 0 .45rem;
    background: none;
    border: 1px solid var(--cairn-edge);
    border-radius: 4px;
}

.cairn-slim-toggle span {
    display: block;
    height: 2px;
    background: var(--cairn-ink);
}

@media (min-width: 992px) {
    /* Bootstrap's collapse hides it; at this width it is simply a row. */
    .cairn-slim-nav.collapse:not(.show) {
        display: flex;
    }
}

@media (max-width: 991.98px) {
    /* On a phone the bar is pure overhead — the stage has a few hundred pixels
       of height to divide between the story and the map — so it keeps the way
       home, hands the links to the toggle, and takes as little as it can. */
    .cairn-playing {
        --cairn-topbar: 2.5rem;
    }

    .cairn-slim-bar {
        gap: .75rem;
        padding: 0 .6rem;
    }

    .cairn-slim-brand {
        font-size: 1.05rem;
    }

    .cairn-slim-toggle {
        display: flex;
    }

    /* Over the stage, not above it. In the flow this panel would push the map
       down, which resizes Leaflet's container for as long as somebody reads
       five links and then resizes it back. */
    .cairn-slim-nav.collapse.show,
    .cairn-slim-nav.collapsing {
        display: flex;
        position: absolute;
        top: 100%;
        right: .5rem;
        z-index: 1000;
        flex: none;
        flex-direction: column;
        gap: .85rem;
        min-width: 12rem;
        padding: .85rem 1rem;
        background: var(--cairn-parchment);
        border: 1px solid var(--cairn-edge);
        border-top: 0;
        border-radius: 0 0 6px 6px;
        box-shadow: 0 12px 28px rgba(43, 43, 40, .18);
    }

    .cairn-slim-nav.collapsing {
        overflow: hidden;
    }
}

/* Rail and map. They scroll independently: the story is long and the map must
   not travel with it. */
.cairn-stage {
    display: grid;
    grid-template-columns: 1fr;
    height: 100%;
}

.cairn-rail {
    overflow-y: auto;
    /* The rail is a fixed column, so anything a shade too wide — a long word, a
       list's default indent — buys it a horizontal scrollbar it can never
       usefully use. */
    overflow-x: hidden;
    padding: 0 1.25rem 1.25rem;
    /* The same veil the content of every other page sits on. Without it the
       story is small text directly on the artwork, which is exactly where the
       engraving is busiest. */
    background: rgba(246, 242, 233, .84);
}

.cairn-rail .cairn-guess-log {
    list-style: none;
    padding: 0;
    margin: 0 0 1rem;
}

.cairn-rail .cairn-guess-log li {
    border-left: 3px solid var(--cairn-edge);
    padding-left: .75rem;
    margin-bottom: .75rem;
}

/* The year sticks to the top of the rail. Everything else here can scroll
   away; the year cannot, because the whole puzzle is built on it being on
   screen from the first second to the last.

   Being stuck, it has to hide what passes underneath it, and what passes
   underneath is not only text — the scene painting scrolls up through here, and
   a veil that reads as opaque over a paragraph is a window over a photograph.
   So this one is solid. It is the same parchment the veil is made of, which is
   why there is no visible band where one ends and the other begins. */
.cairn-rail .cairn-quest-header {
    position: sticky;
    top: 0;
    z-index: 5;
    background: var(--cairn-parchment);
    /* Out to the rail's own edges. With the rail's padding left in place the
       background would stop 1.25rem short on each side and the scene would
       scroll up through the gap. */
    margin: 0 -1.25rem;
    padding: .85rem 1.25rem .7rem;
    border-bottom: 1px solid var(--cairn-edge);
}

/* Once it is stuck to the top of a 400px rail, the quest number is noise
   between the title and the story. */
.cairn-rail .cairn-quest-number {
    display: none;
}

/* A quest that has been announced but has not opened has no rail — there is no
   map to sit beside — so the veil the rail would have given it has to come from
   somewhere. The two blocks carry it between them rather than sharing a
   wrapper: they are the only children of .cairn-quest in this state, and a
   wrapper element would exist purely to be a background.

   The `>` matters. While playing, the header is inside the rail and none of
   this applies to it. */
.cairn-quest-main > .cairn-quest:has(> .cairn-hero) {
    overflow-y: auto;
    padding: 1.5rem 1rem 2.5rem;
}

.cairn-quest-main > .cairn-quest > .cairn-quest-header,
.cairn-quest-main > .cairn-quest > .cairn-hero {
    max-width: 42rem;
    margin: 0 auto;
    padding-left: 1.75rem;
    padding-right: 1.75rem;
    background: rgba(246, 242, 233, .84);
    border-left: 1px solid var(--cairn-edge);
    border-right: 1px solid var(--cairn-edge);
}

.cairn-quest-main > .cairn-quest > .cairn-quest-header {
    padding-top: 1.75rem;
    border-top: 1px solid var(--cairn-edge);
    border-radius: 6px 6px 0 0;
}

.cairn-quest-main > .cairn-quest > .cairn-hero {
    padding-top: 0;
    border-bottom: 1px solid var(--cairn-edge);
    border-radius: 0 0 6px 6px;
}

.cairn-mapwrap {
    position: relative;
    min-height: 20rem;
}

@media (min-width: 992px) {
    .cairn-stage {
        /* Wide enough for a hint panel to read as a paragraph rather than a
           column of two-word lines, and no wider — the rest is map. */
        grid-template-columns: minmax(20rem, 25rem) 1fr;
    }

    .cairn-rail {
        border-right: 1px solid var(--cairn-edge);
    }

    /* The navigator and the panel's furniture belong to the phone layout. */
    .cairn-strip,
    .cairn-sheet-bar {
        display: none;
    }
}

/* ---- the phone layout ----
 *
 * Splitting a 390px screen between a rail and a map gives two things that are
 * each too small to use. So the map takes all of it, and the story becomes six
 * square tiles above it — the quest, its picture, the three panels, and where
 * you have got to — each opening over the map and closing again.
 *
 * Six across a phone makes each tile about 65px, which is why the row is the
 * height it is: the tiles are square by construction, and the row is however
 * tall a sixth of the screen happens to be.
 */
@media (max-width: 991.98px) {
    .cairn-stage {
        position: relative;
        grid-template-rows: auto 1fr;
    }

    .cairn-strip {
        grid-row: 1;
        display: flex;
        gap: 2px;
        padding: 2px;
        background: var(--cairn-parchment);
        border-bottom: 1px solid var(--cairn-edge);
    }

    .cairn-tile {
        /* `1 1 0` and not `1 1 auto`: a tile's contents must not get a vote on
           how wide it is, or the one holding a four-digit year comes out wider
           than the rest and the row stops being a grid of squares. */
        flex: 1 1 0;
        min-width: 0;
        aspect-ratio: 1;
        position: relative;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: .1rem;
        overflow: hidden;
        padding: .15rem;
        background: rgba(246, 242, 233, .92);
        border: 1px solid var(--cairn-edge);
        border-radius: 4px;
        color: var(--cairn-ink);
        line-height: 1.05;
    }

    .cairn-tile img {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        object-fit: cover;
    }

    /* Over a photograph, and legible on it. */
    .cairn-tile img + .cairn-tile-label {
        position: relative;
        color: #fff;
        background: rgba(43, 43, 40, .6);
        border-radius: 3px;
        padding: .05rem .25rem;
    }

    .cairn-tile-label {
        font-size: .5rem;
        letter-spacing: .1em;
        text-transform: uppercase;
        color: var(--cairn-stone);
        white-space: nowrap;
    }

    .cairn-tile-year-label {
        font-size: .45rem;
        letter-spacing: .12em;
        text-transform: uppercase;
        color: var(--cairn-stone);
    }

    .cairn-tile-year-num {
        font-family: var(--cairn-font-display);
        font-size: 1.15rem;
        color: var(--cairn-found);
    }

    .cairn-tile-flag {
        font-size: .4rem;
        letter-spacing: .08em;
        text-transform: uppercase;
        color: #fff;
        background: var(--cairn-found);
        border-radius: 999px;
        padding: .05rem .3rem;
    }

    .cairn-tile-flag-quiet {
        background: var(--cairn-stone);
    }

    .cairn-tile-count {
        font-family: var(--cairn-font-display);
        font-size: 1.05rem;
        font-variant-numeric: tabular-nums;
    }

    /* A panel that has not been turned to yet. Dashed, like its locked card. */
    .cairn-tile.is-locked {
        border-style: dashed;
        background: rgba(255, 255, 255, .6);
        color: var(--cairn-stone);
    }

    /* Whichever panel is open. */
    .cairn-tile.is-active {
        border-color: var(--cairn-ink);
        box-shadow: 0 0 0 2px rgba(43, 43, 40, .18);
    }

    /* The panel a confirm has just turned to, until it has been read. */
    .cairn-tile.is-new {
        border-color: var(--cairn-warm);
        box-shadow: 0 0 0 2px rgba(141, 110, 99, .35);
        animation: cairn-tile-pulse 1.8s ease-in-out infinite;
    }

    @keyframes cairn-tile-pulse {
        0%, 100% { box-shadow: 0 0 0 0 rgba(141, 110, 99, .55); }
        50% { box-shadow: 0 0 0 .3rem rgba(141, 110, 99, 0); }
    }

    /* The map has the whole of the rest. */
    .cairn-mapwrap {
        grid-row: 2;
        grid-column: 1;
        min-height: 0;
    }

    /* The rail is no longer a column beside the map: it is a sheet over it,
       showing one panel at a time. In the same grid cell as the map rather
       than positioned over it, so it is exactly the size of what it covers
       without anything having to be measured. */
    .cairn-rail {
        grid-row: 2;
        grid-column: 1;
        z-index: 950;
        display: none;
        padding: 0 1rem 1.5rem;
        /* Opaque. Everywhere else on the site a veil lets the artwork through;
           here what is behind is a map, and a map showing through a paragraph
           is just noise. */
        background: var(--cairn-parchment);
    }

    .cairn-rail.is-open {
        display: block;
    }

    /* While a panel is open the map is not being used, and everything that
       belongs to the map goes with it.

       Leaflet puts its control corners at z-index 1000 — the controls
       themselves are 800, the corners that hold them are not — so the zoom
       buttons drew straight over the sheet and sat on top of its own prev/next.
       Raising the sheet above 1000 would only move the argument to the top bar,
       and the honest answer is that a covered map should not be offering
       controls at all. `visibility` rather than `display`: Leaflet measures
       these, and a control with no box comes back wrong when the map is next
       invalidated. */
    .cairn-sheet-open .leaflet-control-container,
    .cairn-sheet-open .cairn-scorebar,
    .cairn-sheet-open .cairn-controls {
        visibility: hidden;
    }

    /* `contents` rather than `block`: the hints wrapper would otherwise have to
       be shown and hidden alongside whichever of its children is on, and this
       way there is only ever one thing deciding what is visible. */
    .cairn-rail .cairn-hints {
        display: contents;
    }

    .cairn-rail [data-part] {
        display: none;
    }

    .cairn-rail [data-part].is-shown {
        display: block;
    }

    /* Nothing sticks on a phone: a panel is the whole sheet, and the year is on
       a tile that never goes anywhere. */
    .cairn-rail .cairn-quest-header {
        position: static;
        margin: 0;
        background: none;
        border-bottom: 0;
    }

    .cairn-rail .cairn-hint {
        border: 0;
        background: none;
        padding: 0;
    }

    .cairn-sheet-bar {
        position: sticky;
        top: 0;
        z-index: 2;
        display: flex;
        align-items: center;
        gap: .35rem;
        padding: .4rem 0;
        background: rgba(246, 242, 233, .97);
    }

    .cairn-sheet-nav,
    .cairn-sheet-close {
        width: 2.2rem;
        height: 2.2rem;
        line-height: 1;
        font-size: 1.35rem;
        color: var(--cairn-stone);
        background: rgba(255, 255, 255, .7);
        border: 1px solid var(--cairn-edge);
        border-radius: 50%;
    }

    .cairn-sheet-nav:disabled {
        opacity: .3;
    }

    .cairn-sheet-close {
        margin-left: auto;
    }

    /* The moment stays above the sheet: it is the answer to the confirm that
       opened it, and a sheet arriving on top of it would swallow the only time
       it is ever said. */
    .cairn-flash.cairn-over-map {
        z-index: 970;
    }

    /* With a panel open it drops to the foot of the screen. It normally sits
       clear of the confirm button, and with the map covered that button is not
       there — so the space it was avoiding is the space it should use, rather
       than lying across the middle of what the player is reading. */
    .cairn-sheet-open .cairn-flash.cairn-over-map {
        bottom: .75rem;
    }
}

/* Anything sitting on the map. Leaflet's own controls are at z-index 800 and
   its popups at 700, so these have to clear both. */
.cairn-over-map {
    position: absolute;
    z-index: 900;
    background: rgba(255, 255, 255, .92);
    border: 1px solid var(--cairn-edge);
    border-radius: 4px;
    padding: .4rem .75rem;
    backdrop-filter: blur(2px);
}

.cairn-scorebar.cairn-over-map {
    top: .75rem;
    right: .75rem;
    gap: 1rem;
}

.cairn-controls.cairn-over-map {
    bottom: 1rem;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: .75rem;
    /* One row. Two rows of floating chrome eat more map than the give-up link
       is worth, and the bar jumping taller as the help text changes length is
       movement over a map somebody is reading. */
    flex-wrap: nowrap;
    white-space: nowrap;
    max-width: calc(100% - 2rem);
    padding: .4rem .5rem .4rem .4rem;
}

/* The moment, over the map, directly above the button that produced it.
   Centred without a transform: jQuery UI wraps an element to animate it, and a
   wrapper does not carry a `translateX(-50%)` — the panel would arrive half a
   width to the right of where it settles. `left/right: 0` with auto margins
   centres a fit-content box and survives being wrapped. */
.cairn-flash.cairn-over-map {
    display: none;
    bottom: 4.5rem;
    left: 0;
    right: 0;
    width: fit-content;
    max-width: calc(100% - 2rem);
    margin: 0 auto;
    padding: .7rem 2.2rem .7rem .9rem;
    line-height: 1.45;
    box-shadow: 0 10px 28px rgba(43, 43, 40, .18);
}

.cairn-flash-label {
    display: block;
    font-size: .7rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--cairn-stone);
}

.cairn-flash-text {
    color: var(--cairn-warm);
}

.cairn-flash-close {
    position: absolute;
    top: .15rem;
    right: .35rem;
    width: 1.6rem;
    height: 1.6rem;
    line-height: 1;
    font-size: 1.2rem;
    border: 0;
    background: none;
    color: var(--cairn-stone);
}

.cairn-flash-close:hover {
    color: var(--cairn-ink);
}

.cairn-controls.cairn-over-map .btn-link {
    padding-left: .25rem;
    padding-right: .25rem;
    font-size: .875rem;
}

#cairn-map-help {
    font-size: .875rem;
    /* A coordinate readout is fixed-width by nature; the tabular figures stop
       the bar twitching as the numbers change under a dragging thumb. */
    font-variant-numeric: tabular-nums;
}

/* Honest, not alarming: the point is that a guest knows the deal before it
   costs them anything, not that they are nagged on every screen. */
.cairn-guest-note {
    font-size: .8rem;
    line-height: 1.45;
    color: var(--cairn-stone);
    background: var(--cairn-parchment);
    border-radius: 4px;
    padding: .6rem .75rem;
    margin-bottom: 1rem;
}

.cairn-log-label {
    display: block;
    font-size: .7rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--cairn-stone);
}

/* The map has to have a height in CSS: Leaflet measures its container, and a
   container with no height renders a map one pixel tall. Inside the stage it
   fills its cell instead of standing at a fixed height. */
.cairn-mapwrap .cairn-map {
    position: absolute;
    inset: 0;
    height: 100%;
    border: 0;
    border-radius: 0;
}

.cairn-map {
    height: 32rem;
    border: 1px solid var(--cairn-edge);
    border-radius: 4px;
}

.cairn-map-small {
    height: 20rem;
    margin: 1.5rem 0;
}

/* The distance printed along the line between where the answer was and where
   the player put their marker. A permanent tooltip, so it is read rather than
   clicked for — it is the one fact that picture exists to tell them.

   Leaflet's own tooltip skin is a white callout with a pointer, which reads as
   a popup somebody forgot to close. This is a label on a line. */
.leaflet-tooltip.cairn-map-distance {
    background: var(--cairn-parchment);
    border: 1px solid var(--cairn-edge);
    border-radius: 3px;
    box-shadow: 0 1px 4px rgba(43, 43, 40, .18);
    color: var(--cairn-ink);
    font-family: var(--cairn-font-text);
    font-size: .8rem;
    font-weight: 600;
    padding: .1rem .4rem;
    white-space: nowrap;
}

/* Its callout pointer, which a label in the middle of a line has no use for. */
.leaflet-tooltip.cairn-map-distance::before {
    display: none;
}

@media (max-width: 991px) {
    .cairn-map {
        height: 22rem;
    }
}

.cairn-controls {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin: .75rem 0;
    flex-wrap: wrap;
}

.cairn-guess-log {
    list-style: none;
    padding: 0;
    margin: 0;
}

.cairn-guess-log li {
    padding: .35rem 0;
    border-bottom: 1px solid var(--cairn-edge);
    color: var(--cairn-warm);
}

.cairn-guess-correct {
    color: var(--cairn-found);
    font-weight: 600;
}

.cairn-reveal {
    border-top: 3px solid var(--cairn-ink);
    padding-top: 1rem;
    margin-top: 1.5rem;
}

.cairn-score {
    font-size: 2rem;
    font-weight: 700;
    color: var(--cairn-found);
    margin: 0;
}

/* How far out they were, in words. A notch above the muted lines around it,
   because it is the sentence most players actually go looking for, and the one
   the line drawn across the map is also saying. */
.cairn-how-close {
    color: var(--cairn-warm);
    font-weight: 600;
}

.cairn-signup-prompt {
    background: #fff;
    border: 1px dashed var(--cairn-edge);
    border-radius: 4px;
    padding: .75rem 1rem;
}

/* ---- lists and boards ---- */

.cairn-filter {
    max-width: 18rem;
    margin: 1rem 0 2rem;
}

.cairn-pagination {
    display: flex;
    gap: 1.5rem;
    align-items: center;
    margin: 2rem 0;
}

.cairn-board td,
.cairn-board th {
    vertical-align: middle;
}

.cairn-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 2rem;
    margin: 1.5rem 0;
}

.cairn-stat {
    text-align: center;
}

.cairn-stat-value {
    display: block;
    font-size: 2rem;
    font-weight: 700;
    font-family: "Playfair Display", Georgia, serif;
}

.cairn-stat-label {
    display: block;
    font-size: .8rem;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--cairn-stone);
}

/* A grid rather than `columns: 2`. Multi-column flows rows of uneven height
   into ragged pairs, and rows became uneven the moment some of them grew a
   thumbnail and some did not. Auto-fill also drops to one column on a phone
   without a media query. */
.cairn-place-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
    gap: .4rem 1.5rem;
}

.cairn-place-list li {
    padding: .3rem 0;
}

.cairn-place-meta {
    color: var(--cairn-stone);
}

/* The article's picture: usually a photograph of the place from Commons. Given
   a height cap because a portrait-format photograph of a tower would otherwise
   push everything worth reading below the fold. */
.cairn-place-figure {
    margin: 0 0 1.25rem;
}

.cairn-place-figure img {
    width: 100%;
    max-height: 26rem;
    object-fit: cover;
    border-radius: 4px;
    border: 1px solid var(--cairn-edge);
}

.cairn-place-figure figcaption {
    margin-top: .35rem;
}

/* A thumbnail beside each place in the collection, where there is one. The row
   stays readable without it — most of the bank will have none for a while. */
.cairn-place-row {
    display: flex;
    align-items: center;
    gap: .6rem;
}

.cairn-place-thumb img {
    width: 3.5rem;
    height: 3.5rem;
    object-fit: cover;
    border-radius: 3px;
    border: 1px solid var(--cairn-edge);
    display: block;
}

/* The picture panel in the places manager wants a preview with a real height,
   the same way the builder's does. */
.cairn-place-picture .cairn-picture-preview img {
    max-height: 18rem;
    object-fit: contain;
}

.cairn-pipeline {
    background: var(--cairn-parchment);
    border: 1px solid var(--cairn-edge);
    padding: 1rem;
    border-radius: 4px;
    /* It is a column-aligned block of preformatted text, so it cannot reflow.
       Scrolling it inside its own box is what stops it widening the page and
       taking every other element sideways with it. */
    overflow-x: auto;
}

/* An open draft that will overwrite a published quest rather than add one. */
.cairn-badge-revision {
    background: var(--cairn-parchment);
    border: 1px solid var(--cairn-edge);
    color: var(--cairn-stone);
    font-family: inherit;
    font-weight: 400;
    font-size: .7rem;
    vertical-align: middle;
}

/* The quest table gained an Edit column and had none to spare. The status
   select was already being squeezed to `liv⌄`; below this width it stops
   saying which status it is holding, which is the only thing it is for. */
.cairn-admin-table select[name="status"] {
    min-width: 7.5rem;
}

.cairn-roll-form {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem 1rem;
}

/* ---- an editor's table on a phone ----

   Nine columns of quest do not cross a phone, and `table-responsive` is the
   wrong fix here: it scrolls the status control out of sight of the title it
   belongs to, which is the one pairing this page exists to show. So below the
   `lg` breakpoint each row becomes a card and the column headings are
   reprinted beside their values, out of the `data-label` on every cell.

   991.98px and not the narrower tablet breakpoint because the quest row holds a
   date field, a select and nine columns: at 768px it still does not fit, and the
   title column is squeezed to one word per line. It is also the breakpoint the
   rest of this stylesheet turns on. Above it, nothing here applies and the table
   is an ordinary table. */

@media (max-width: 991.98px) {
    .cairn-admin-table,
    .cairn-admin-table tbody,
    .cairn-admin-table tr,
    .cairn-admin-table td {
        display: block;
        width: auto;
    }

    /* The headings are on every cell now. */
    .cairn-admin-table thead {
        display: none;
    }

    .cairn-admin-table tr {
        display: flex;
        flex-wrap: wrap;
        align-items: baseline;
        gap: .4rem 1rem;
        background: #fff;
        border: 1px solid var(--cairn-edge);
        border-radius: 4px;
        padding: .75rem;
        margin-bottom: .75rem;
    }

    .cairn-admin-table td {
        display: flex;
        align-items: baseline;
        gap: .4rem;
        border: 0;
        padding: 0;
    }

    .cairn-admin-table td::before {
        content: attr(data-label);
        flex: none;
        font-size: .7rem;
        letter-spacing: .1em;
        text-transform: uppercase;
        color: var(--cairn-stone);
    }

    /* A cell with nothing to label — the discard button — would otherwise get
       an empty box and the gap either side of it. */
    .cairn-admin-table td:not([data-label])::before {
        content: none;
    }

    /* The title is the card's heading rather than another labelled field, and
       it goes first whatever column it sits in on the desktop. */
    .cairn-admin-table td.cairn-cell-title {
        order: -1;
        width: 100%;
        font-family: "Playfair Display", Georgia, serif;
        font-size: 1.05rem;
        line-height: 1.3;
    }

    /* The two cells holding a form. They need the width for a date field and a
       select, and they read as the actions at the foot of the card. */
    .cairn-admin-table td.cairn-cell-wide {
        flex-direction: column;
        align-items: stretch;
        width: 100%;
        gap: .15rem;
    }

    .cairn-admin-table td.cairn-cell-wide form,
    .cairn-admin-table td.cairn-cell-wide select {
        width: 100%;
    }

    .cairn-admin-table td.cairn-cell-action {
        width: 100%;
        margin-top: .25rem;
        border-top: 1px solid var(--cairn-edge);
        padding-top: .5rem;
    }
}

/* ---- the editor's frame ---- */

.cairn-admin-header {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    border-bottom: 2px solid var(--cairn-ink);
    padding: 1rem 0;
    margin-bottom: 1.5rem;
}

.cairn-admin-brand strong {
    font-family: "Playfair Display", Georgia, serif;
    font-size: 1.3rem;
    margin-right: .5rem;
}

.cairn-admin-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
}

.cairn-admin-nav a {
    color: var(--cairn-stone);
    text-decoration: none;
}

.cairn-admin-nav a:hover,
.cairn-admin-nav a.active {
    color: var(--cairn-ink);
    text-decoration: underline;
}

/* ---- the video scripts ---- */

/* A script is written to be read whole and then copied out, so the box is tall
   and monospaced: the timings are laid out in columns and a proportional face
   pulls them out of line. */
.cairn-video-text {
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    font-size: .85rem;
    line-height: 1.5;
    white-space: pre-wrap;
}

.cairn-video-clip {
    background: var(--cairn-parchment);
    border: 1px solid var(--cairn-edge);
    border-radius: 4px;
    padding: 1rem;
}

/* One scene of a multi-scene script: a whole script of its own, generated on
   its own, and pasted into the video tool on its own. The rule down the left is
   .cairn-panel-edit's, for the same reason — a stack of separately editable
   blocks needs a visible edge or an editor loses track of which box they are
   copying. */
.cairn-video-scene {
    border-left: 3px solid var(--cairn-edge);
    padding-left: 1rem;
}

/* Vertical, because everything made from these scripts is 9:16. Capped so a
   portrait clip cannot push the whole column past the fold on a laptop. */
.cairn-video-player video {
    max-height: 60vh;
    background: #000;
    border-radius: 4px;
    display: block;
    margin-inline: auto;
}

/* ---- the quest builder ---- */

.cairn-build-form {
    max-width: 46rem;
}

.cairn-quest-form fieldset {
    border: 1px solid var(--cairn-edge);
    border-radius: 4px;
    padding: 1rem 1.25rem;
    background: #fff;
}

.cairn-quest-form legend {
    font-family: "Playfair Display", Georgia, serif;
    width: auto;
    padding: 0 .5rem;
}

.cairn-panel-edit {
    border-left: 3px solid var(--cairn-edge);
    padding-left: 1rem;
    margin-bottom: 1.5rem;
}

.cairn-picture {
    background: var(--cairn-parchment);
    border-radius: 4px;
    padding: .75rem;
    margin-bottom: .5rem;
}

.cairn-picture-preview img {
    max-width: 100%;
    height: auto;
    border-radius: 3px;
}

/* A placeholder with a real height, so generating a picture does not make the
   whole form jump a screen's worth when it arrives. */
.cairn-picture-placeholder {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    align-items: center;
    justify-content: center;
    height: 8rem;
    border: 1px dashed var(--cairn-edge);
    border-radius: 3px;
    background: #fff;
}

/* Wikimedia search results. A grid of contact-sheet thumbnails: choosing a
   picture is a visual job, and a list of filenames is not how anybody does it.
   `contain` rather than `cover` because a Commons search returns portraits,
   panoramas and maps in the same twelve results, and cropping is what makes a
   map unrecognisable at this size. */
.cairn-wiki-results {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
    gap: .5rem;
}

.cairn-wiki-result {
    display: flex;
    flex-direction: column;
    gap: .25rem;
    padding: .25rem;
    text-align: left;
    background: #fff;
    border: 1px solid var(--cairn-edge);
    border-radius: 3px;
    cursor: pointer;
}

.cairn-wiki-result img {
    width: 100%;
    height: 7rem;
    object-fit: contain;
    background: var(--cairn-parchment);
}

/* The chosen one has to read at a glance from across the grid, so it is a ring
   and a tint rather than a border that only changes colour. */
.cairn-wiki-result.chosen {
    border-color: var(--cairn-stone);
    box-shadow: 0 0 0 2px var(--cairn-stone);
}

.cairn-wiki-caption,
.cairn-wiki-licence {
    font-size: .7rem;
    line-height: 1.25;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

.cairn-wiki-licence {
    color: var(--cairn-stone);
    -webkit-line-clamp: 1;
}

/* Drawing a panel takes the better part of a minute, which is long enough that
   a line of text changing is easy to miss and easy to read as "nothing
   happened". Its own spinner rather than Bootstrap's, so the one visible sign
   that the builder is working does not depend on a CDN class name. */
.cairn-spinner {
    display: inline-block;
    width: 1em;
    height: 1em;
    vertical-align: -.125em;
    border: 2px solid var(--cairn-edge);
    border-top-color: var(--cairn-stone);
    border-radius: 50%;
    animation: cairn-spin .7s linear infinite;
}

.cairn-spinner-lg {
    width: 1.75rem;
    height: 1.75rem;
    border-width: 3px;
}

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

/* Someone who has asked not to be shown motion still needs to know the thing
   is working, so it pulses instead of turning. */
@media (prefers-reduced-motion: reduce) {
    .cairn-spinner {
        animation: cairn-pulse 1.4s ease-in-out infinite;
    }

    @keyframes cairn-pulse {
        50% { opacity: .25; }
    }
}

/* The conversation sticks while the form scrolls: the two halves of this screen
   are read against each other, and a chat that scrolls away is a chat nobody
   uses. */
.cairn-chat {
    position: sticky;
    top: 1rem;
}

.cairn-chat-log {
    height: 26rem;
    overflow-y: auto;
    border: 1px solid var(--cairn-edge);
    border-radius: 4px;
    padding: .75rem;
    background: #fff;
    margin-bottom: .75rem;
}

.cairn-chat-line {
    margin-bottom: .75rem;
}

.cairn-chat-line p {
    margin: 0;
}

.cairn-chat-who {
    display: block;
    font-size: .7rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--cairn-stone);
}

.cairn-chat-user p {
    background: var(--cairn-parchment);
    border-radius: 4px;
    padding: .5rem .75rem;
}

.cairn-chat-error p {
    color: #a4342c;
}

.cairn-preview,
.cairn-preview-admin {
    border: 1px solid var(--cairn-edge);
    border-radius: 4px;
    padding: 1.25rem;
    background: #fff;
}

/* ---- sharing ----
 *
 * The row under a reveal, on a place article and on a shared result page.
 * resources/views/_shared/share.htm, driven by public/js/share.js.
 *
 * The copyable block is styled as a thing to be taken rather than a thing to be
 * read: monospaced, boxed, and wide enough that the squares sit on one line.
 * That block is the part that actually spreads a daily game, so it is at the
 * top of the row and it is what the primary button copies. */

.cairn-share {
    margin: 2rem 0 1rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--cairn-edge);
}

.cairn-share-heading {
    font-size: .78rem;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--cairn-stone);
    margin-bottom: .75rem;
}

.cairn-share-block {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .95rem;
    line-height: 1.5;
    white-space: pre-wrap;
    /* A pasted link runs past the box on a phone otherwise, and a block that
       has to be scrolled sideways to be read is one nobody copies. */
    overflow-wrap: anywhere;
    background: var(--cairn-parchment);
    border: 1px solid var(--cairn-edge);
    border-radius: 4px;
    padding: .75rem .9rem;
    margin-bottom: .75rem;
}

.cairn-share-blockaction {
    text-align: center;
    margin-bottom: 0;
}

/* Centred, because this row is the call to action rather than a toolbar. A
   left-aligned row of five reads as site furniture and gets skipped. */
.cairn-share-buttons {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: .5rem;
    margin-top: 1rem;
}

/* Each button is its mark over its name. The mark is what is actually
   recognised — nobody reads a row of share buttons, they look for the green
   one — and the name under it is what makes the row usable to everyone who
   does not, a screen reader included.

   Deliberately not Bootstrap's .btn: these have to be the same size whether
   the label is "X" or "WhatsApp", which a text button never is. */
.cairn-share-btn {
    --cairn-share-brand: var(--cairn-ink);

    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .3rem;
    flex: 0 1 auto;
    width: 4.6rem;
    padding: .6rem .25rem .5rem;
    border: 1px solid var(--cairn-edge);
    border-radius: 6px;
    background: rgba(255, 255, 255, .55);
    color: var(--cairn-ink);
    font-size: .72rem;
    line-height: 1.2;
    text-align: center;
    text-decoration: none;
    transition: border-color .15s, background-color .15s, transform .15s;
}

.cairn-share-btn:hover,
.cairn-share-btn:focus-visible {
    border-color: var(--cairn-share-brand);
    background: #fff;
    color: var(--cairn-ink);
    text-decoration: none;
    transform: translateY(-1px);
}

.cairn-share-icon {
    width: 1.35rem;
    height: 1.35rem;
    /* The marks are single-path silhouettes, so the brand colour is the fill
       and there is nothing else to tint. */
    fill: var(--cairn-share-brand);
}

.cairn-share-icon-fa {
    font-size: 1.2rem;
    line-height: 1.35rem;
    color: var(--cairn-share-brand);
}

/* The brand colours, and only on the mark. A row of solid brand-coloured
   buttons is five loud rectangles on a parchment page; the mark alone is
   recognised just as fast and lets the row belong to the site it is on. */
.cairn-share-whatsapp  { --cairn-share-brand: #25d366; }
.cairn-share-facebook  { --cairn-share-brand: #0866ff; }
.cairn-share-twitter   { --cairn-share-brand: #000; }
.cairn-share-bluesky   { --cairn-share-brand: #0285ff; }
.cairn-share-threads   { --cairn-share-brand: #000; }
.cairn-share-telegram  { --cairn-share-brand: #26a5e4; }
.cairn-share-reddit    { --cairn-share-brand: #ff4500; }
.cairn-share-pinterest { --cairn-share-brand: #bd081c; }
.cairn-share-native,
.cairn-share-copy-btn  { --cairn-share-brand: var(--cairn-stone); }

.cairn-share-said {
    min-height: 1.25rem;
    margin: .5rem 0 0;
    font-size: .85rem;
    color: var(--cairn-found);
}

.cairn-share-note {
    margin-top: .5rem;
}

/* ---- a shared result ----
 *
 * The page a stranger lands on. It has one job, which is the button in the
 * middle of it, so everything above is kept to a glance. */

.cairn-shared {
    max-width: 46rem;
    margin: 0 auto;
    padding: 1.5rem 0 3rem;
}

.cairn-shared-head {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1rem;
}

.cairn-shared-avatar {
    width: 88px;
    height: 88px;
    border-radius: 50%;
    object-fit: cover;
    border: 2px solid var(--cairn-ink);
    flex: 0 0 auto;
}

/* The fallback when a member has no picture. A drawn initial rather than a
   silhouette: a grey person-shaped icon says "no picture", where a letter in
   the site's own face says "this person". */
.cairn-shared-monogram {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--cairn-parchment);
    font-family: var(--cairn-font-display);
    font-size: 2.4rem;
    color: var(--cairn-ink);
    line-height: 1;
}

.cairn-shared-heading {
    font-size: 1.5rem;
    margin: 0;
}

.cairn-shared-score {
    font-size: 1.1rem;
    margin-bottom: .25rem;
}

.cairn-shared-score strong {
    font-family: var(--cairn-font-display);
    font-size: 3rem;
    line-height: 1;
    margin-right: .35rem;
}

.cairn-shared-firstsight {
    margin-left: .75rem;
    font-style: normal;
    font-size: .78rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--cairn-stone);
}

/* The checkpoint squares. Loosened up, because four emoji set solid read as one
   smudge at body size. */
.cairn-shared-trace {
    font-size: 1.6rem;
    letter-spacing: .2em;
    margin: .25rem 0 1.5rem;
}

.cairn-shared-quest {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 1.5rem;
    align-items: center;
    padding: 1.25rem;
    background: var(--cairn-parchment);
    border: 1px solid var(--cairn-edge);
    border-radius: 4px;
}

.cairn-shared-art img {
    width: 100%;
    height: auto;
    border-radius: 3px;
    display: block;
}

.cairn-shared-title {
    font-size: 1.35rem;
    margin: .25rem 0 .75rem;
}

.cairn-shared-nospoilers {
    color: var(--cairn-stone);
    font-size: .95rem;
}

.cairn-shared-alt {
    margin: .75rem 0 0;
    font-size: .9rem;
}

.cairn-shared-what {
    margin-top: 1.5rem;
    color: var(--cairn-stone);
}

@media (max-width: 640px) {
    .cairn-shared-quest {
        grid-template-columns: minmax(0, 1fr);
    }

    .cairn-shared-score strong {
        font-size: 2.4rem;
    }
}

/* ---- the reveal, over the map ----
 *
 * A desktop-only panel carrying a copy of what is at the foot of the rail. The
 * rail's copy is the record — it keeps the story, the summary and the picture
 * credits — and this one is the moment: the answer and the share row, where the
 * player's eyes already are after the last confirm.
 *
 * Absolutely positioned inside .cairn-mapwrap rather than fixed to the viewport,
 * so it covers the map and leaves the rail readable beside it. The map under it
 * is drawing the answer and the player's own marker joined by a dashed line,
 * which is the payoff — which is why this closes, and why the scorebar keeps a
 * button to bring it back.
 */
.cairn-resultpanel {
    position: absolute;
    inset: 0;
    z-index: 700;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
}

.cairn-resultpanel[hidden] {
    display: none;
}

.cairn-resultpanel-backdrop {
    position: absolute;
    inset: 0;
    /* Light rather than the usual dark scrim: what is behind it is a map made
       of parchment, and a black wash over it reads as the page having crashed. */
    background: rgba(43, 43, 40, .35);
}

.cairn-resultpanel-card {
    position: relative;
    width: min(34rem, 100%);
    max-height: 100%;
    overflow-y: auto;
    padding: 1.75rem 1.75rem 1.5rem;
    background: var(--cairn-parchment);
    border: 1px solid var(--cairn-edge);
    border-radius: 8px;
    box-shadow: 0 18px 48px rgba(43, 43, 40, .32);
}

.cairn-resultpanel-close {
    position: absolute;
    top: .35rem;
    right: .6rem;
    border: 0;
    background: none;
    font-size: 1.6rem;
    line-height: 1;
    color: var(--cairn-stone);
    cursor: pointer;
}

.cairn-resultpanel-close:hover {
    color: var(--cairn-ink);
}

/* The panel takes focus when it opens, so this ring is on screen every time
   somebody finishes a quest. The browser's default is a blue rectangle around
   a serif × on parchment; this is the same affordance in the site's own ink. */
.cairn-resultpanel-close:focus-visible {
    outline: 2px solid var(--cairn-stone);
    outline-offset: 2px;
    border-radius: 4px;
    color: var(--cairn-ink);
}

.cairn-resultpanel-body > h2 {
    margin-top: 0;
    padding-right: 1.5rem;
}

/* The share row is the second half of this panel and needs to read as its own
   section rather than as a footnote to the result. */
.cairn-resultpanel .cairn-share {
    margin-bottom: 0;
}

/* The button in the scorebar that brings the panel back. */
.cairn-scorebar-share {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    margin-left: .5rem;
    padding: .15rem .6rem;
    border: 1px solid var(--cairn-edge);
    border-radius: 999px;
    background: rgba(255, 255, 255, .6);
    color: var(--cairn-ink);
    font-size: .78rem;
    cursor: pointer;
}

.cairn-scorebar-share:hover {
    background: #fff;
    border-color: var(--cairn-stone);
}

.cairn-scorebar-share .cairn-share-icon-fa {
    font-size: .85rem;
    color: var(--cairn-stone);
}

/* Phones keep the rail-as-sheet they already have. A panel over a panel is two
   things to close, and the sheet opens straight onto the result anyway — the
   last tile in the strip is the score. */
@media (max-width: 991.98px) {
    .cairn-resultpanel,
    .cairn-scorebar-share {
        display: none !important;
    }
}

/* The Share button on a row of the history. Quiet — the row is about the quest
   and its answer; this is an afterthought until somebody wants it. */
.cairn-history-share {
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    margin-top: .35rem;
    padding: .2rem .7rem;
    border: 1px solid var(--cairn-edge);
    border-radius: 999px;
    background: transparent;
    color: var(--cairn-stone);
    font-size: .78rem;
    cursor: pointer;
}

.cairn-history-share:hover {
    border-color: var(--cairn-stone);
    background: rgba(255, 255, 255, .6);
    color: var(--cairn-ink);
}

.cairn-history-share .cairn-share-icon-fa {
    font-size: .8rem;
}

/* Inside a dialog the row has already been announced by the dialog's own
   title, so its own heading is one line of chrome too many — and the dialog is
   narrower than a page, so the buttons come in a size down. Six of them at the
   page size wrap after five and leave one on a row of its own. */
.modal-body .cairn-share {
    margin: 0;
    padding-top: 0;
    border-top: 0;
}

.modal-body .cairn-share-btn {
    width: 4.2rem;
    font-size: .68rem;
}

/* Parchment, like everything else. A white dialog with a parchment-coloured
   block inside it reads as two different sites. */
.cairn-sharedialog .modal-content {
    background: var(--cairn-parchment);
    border: 1px solid var(--cairn-edge);
}

.cairn-sharedialog .modal-header {
    border-bottom-color: var(--cairn-edge);
}

.cairn-sharedialog .modal-title {
    font-family: var(--cairn-font-display);
    font-size: 1.15rem;
}

/* ---- the cookie dialog ----
 *
 * resources/views/_outer/sections/cookie_consent.htm, shown only where there
 * is a measurement id to consent to.
 *
 * A bar across the bottom rather than a modal over the middle. The one screen
 * that matters here is a quest, which is a map and a story, and a dialog that
 * greys the page out before a first-time visitor has seen either is the same
 * decision as asking them to register before they play. The bar is answered in
 * one tap and never comes back on its own.
 *
 * It does not push the page: `fixed`, with the map and the story untouched
 * underneath. A banner that reflows the layout on load moves the button
 * somebody was already reaching for.
 */
.cairn-consent {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1080;
    background: var(--cairn-parchment);
    border-top: 1px solid var(--cairn-edge);
    box-shadow: 0 -6px 18px rgba(43, 43, 40, .12);
    /* Below the home-bar on an iPhone, where the bottom of the viewport is not
       the bottom of the screen and "Necessary only" would sit under it. */
    padding: .9rem 1rem calc(.9rem + env(safe-area-inset-bottom));
    /* The detail panel can outgrow a short phone in landscape, and a Save
       button that cannot be reached is a dialog that cannot be dismissed. On
       iOS `vh` is measured against the viewport with the browser chrome
       retracted, so `svh` is what "short phone" actually means here. */
    max-height: 85vh;
    max-height: 85svh;
    overflow-y: auto;
}

.cairn-consent[hidden],
.cairn-consent-details[hidden] {
    display: none;
}

.cairn-consent-inner,
.cairn-consent-details {
    max-width: 62rem;
    margin: 0 auto;
}

.cairn-consent-inner {
    display: flex;
    align-items: center;
    gap: 1.25rem;
}

.cairn-consent-title {
    display: block;
    font-family: var(--cairn-font-display);
    color: var(--cairn-ink);
}

.cairn-consent-text {
    margin: .2rem 0 0;
    font-size: .9rem;
    color: var(--cairn-stone);
}

.cairn-consent-text a {
    color: var(--cairn-ink);
}

/* The answers carry equal weight, and only the fill differs. Weighting them
   differently is how a dialog stops being a question — see the note in
   cookie_consent.htm. */
.cairn-consent-buttons {
    display: flex;
    gap: .6rem;
    flex-shrink: 0;
}

.cairn-consent .cairn-btn {
    background: var(--cairn-ink);
    border-color: var(--cairn-ink);
    color: var(--cairn-parchment);
    font-size: .95rem;
    padding: .5rem 1.3rem;
}

.cairn-consent .cairn-btn-quiet {
    background: rgba(255, 255, 255, .65);
    border-color: var(--cairn-edge);
    color: var(--cairn-ink);
}

.cairn-consent .cairn-btn-quiet:hover {
    border-color: var(--cairn-stone);
}

/* ---- the per-group detail, behind "Choose" ---- */

.cairn-consent-details {
    margin-top: .9rem;
    padding-top: .9rem;
    border-top: 1px solid var(--cairn-edge);
}

.cairn-consent-group + .cairn-consent-group {
    margin-top: .75rem;
}

.cairn-consent-switch {
    display: flex;
    align-items: center;
    gap: .5rem;
    font-family: var(--cairn-font-display);
    color: var(--cairn-ink);
    cursor: pointer;
}

/* The necessary group is locked on. It still has to read as a real state
   rather than a broken control, so it keeps full contrast and only loses the
   pointer. */
.cairn-consent-switch input[disabled] {
    cursor: default;
}

.cairn-consent-switch input[disabled] + span {
    color: var(--cairn-stone);
}

.cairn-consent-group-text {
    margin: .15rem 0 0 1.6rem;
    font-size: .85rem;
    color: var(--cairn-stone);
}

.cairn-consent-details .cairn-consent-buttons {
    margin-top: .9rem;
}

/* On a phone the row does not fit, and the buttons are what the visitor is
   here to press — so they go full width under the text rather than being
   squeezed to two words each. */
@media (max-width: 640px) {
    .cairn-consent-inner {
        flex-direction: column;
        align-items: stretch;
        gap: .8rem;
    }

    .cairn-consent-buttons {
        flex-wrap: wrap;
    }

    .cairn-consent-buttons .cairn-btn {
        flex: 1 1 8rem;
    }
}
