/* app.css — the Beacon app's pages (login, profile, tickets, seats, admin).
   Same tokens and patterns as home5.css (eyebrows, glowing buttons, cards, the
   dot grid), copied rather than linked so the app can change without touching
   the home page. */

/* Fonts are served from here, not Google Fonts: app pages are personal and
   send nothing to third parties. Latin subset (covers åäö); all three families
   are SIL OFL 1.1 (assets/fonts/README.txt). */
@font-face { font-family: 'Kanit'; font-weight: 400; font-display: swap; src: url(/assets/fonts/kanit-400.woff2) format('woff2'); }
@font-face { font-family: 'Kanit'; font-weight: 700; font-display: swap; src: url(/assets/fonts/kanit-700.woff2) format('woff2'); }
@font-face { font-family: 'Manrope'; font-weight: 200 800; font-display: swap; src: url(/assets/fonts/manrope-variable.woff2) format('woff2'); }
@font-face { font-family: 'JetBrains Mono'; font-weight: 700; font-display: swap; src: url(/assets/fonts/jetbrains-mono-700.woff2) format('woff2'); }

:root {
    --bg: #090e1a;
    --bg-2: #0c1320;
    --surface: #111727;
    --surface-2: #1a2033;
    --hero: linear-gradient(160deg, #1b2235, #111727);
    --accent-border: rgba(255,186,83,0.3);
    --border-soft: rgba(255,255,255,0.08);
    --border: rgba(255,255,255,0.14);
    --text: #EDE9E4;
    --mid: #9aadc6;
    /* Captions. Lifted from #6b7d99 so small text passes 4.5:1 on every surface. */
    --dim: #7a8ca8;
    --yellow: #FFBA53;
    --orange: #FF6842;
    --blue: #249BD7;
    --green: #7ce0a5;
    --glow-y: 0 0 14px rgba(255,186,83,0.65), 0 0 40px rgba(255,186,83,0.18);
    --glow-o: 0 0 14px rgba(255,104,66,0.65), 0 0 40px rgba(255,104,66,0.18);
    --glow-b: 0 0 14px rgba(36,155,215,0.65), 0 0 40px rgba(36,155,215,0.18);
    --glow-g: 0 0 14px rgba(124,224,165,0.45);
    /* Each colour has a job: yellow is coins and what to do next (buttons), blue is
       XP and levels, orange is a warning. Glow is for a moment (a coin landing, an
       unlock, a new level, a button under the pointer), not for what just sits there. */
    --xp: #4cb6ec;
    --xp-light: #8fd6ff;
    --laser: linear-gradient(90deg, transparent, var(--orange) 18%, var(--yellow) 50%, var(--blue) 82%, transparent);
    /* Tints and lines for the seat states, seat badges and done notices. */
    --green-tint: rgba(124,224,165,0.12);
    --green-line: rgba(124,224,165,0.45);
    --yellow-tint: rgba(255,186,83,0.10);
    --orange-tint: rgba(255,104,66,0.06);
    --seat-free-stroke: #7f93b0;
    --seat-taken: #3a4357;
    /* Inputs and the map's background: a shade darker than the page's cards. */
    --field: #0a0f1c;
    --font-body: 'Manrope', system-ui, sans-serif;
    --font-display: 'Kanit', 'Manrope', sans-serif;
    --font-mono: 'JetBrains Mono', ui-monospace, monospace;
}

* { box-sizing: border-box; }
body {
    margin: 0; background: var(--bg); color: var(--text); font: 16px/1.6 var(--font-body);
    -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
}
a { color: var(--yellow); text-underline-offset: 3px; }
:is(a, button, input, select, [tabindex]):focus-visible { outline: 2px solid var(--yellow); outline-offset: 3px; border-radius: 3px; }

/* The dot grid, behind everything. */
.app-dot-bg {
    position: fixed; inset: 0; z-index: 0; pointer-events: none;
    background-image: radial-gradient(rgba(237,233,228,0.08) 1px, transparent 1px);
    background-size: 18px 18px;
}

/* ───────── Header and footer ───────── */
.app-top {
    position: sticky; top: 0; z-index: 50;
    background: rgba(9,14,26,0.72); border-bottom: 1px solid var(--border-soft);
    backdrop-filter: blur(14px) saturate(140%); -webkit-backdrop-filter: blur(14px) saturate(140%);
}
.app-top-inner { display: flex; justify-content: space-between; align-items: center; gap: 16px; max-width: 1000px; height: 64px; margin: 0 auto; padding: 0 16px; }
.app-brand {
    display: inline-flex; align-items: center; gap: 10px;
    font: 700 18px var(--font-display); letter-spacing: 0.08em; color: var(--text); text-decoration: none;
}
.app-brand img { height: 28px; width: auto; filter: drop-shadow(0 0 10px rgba(255,186,83,0.2)); }
.app-top-actions { display: flex; align-items: center; gap: 16px; }
.app-top-actions form { margin: 0; }
.app-top-link {
    font: 700 11px var(--font-mono); letter-spacing: 0.18em; text-transform: uppercase;
    color: var(--mid); text-decoration: none;
}
.app-top-link:hover, .app-top-link[aria-current="page"] { color: var(--yellow); }
.app-top-actions .app-btn { white-space: nowrap; }

/* Content sits above the dot grid. */
.app-shell { position: relative; z-index: 1; display: flex; flex-direction: column; min-height: calc(100vh - 64px); }
/* Room below the content for the fixed tab bar. */
.app-shell-tabs { padding-bottom: calc(72px + env(safe-area-inset-bottom)); }

/* Coming later: shown so the app reads whole, greyed and inert. */
.app-soon { opacity: 0.4; pointer-events: none; user-select: none; }
.app-tag-soon {
    font: 700 10px var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--mid);
    border: 1px dashed var(--border); border-radius: 999px; padding: 2px 8px; white-space: nowrap;
}

/* ───────── Tab bar: the visitor's places (phone) ───────── */
/* Four equal tabs; the current one is yellow with a glowing bar along its top. */
.app-tabs {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
    padding: 0 4px max(10px, env(safe-area-inset-bottom));
    background: rgba(10,15,27,0.97); border-top: 1px solid var(--border-soft);
}
.app-tabs.app-tabs-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.app-tabs > a {
    position: relative; display: flex; flex-direction: column; align-items: center; gap: 5px;
    min-height: 56px; padding: 12px 0 6px;
    font: 600 12px/1 var(--font-body); color: var(--mid); text-decoration: none;
}
.app-tabs .app-icon { width: 24px; height: 24px; }
.app-tabs > a:hover { color: var(--text); }
.app-tabs > a[aria-current] { color: var(--yellow); }
.app-tabs > a[aria-current]::before {
    content: ''; position: absolute; top: -1px; left: 28%; right: 28%; height: 2px; border-radius: 2px;
    background: var(--yellow); box-shadow: 0 0 10px rgba(255,186,83,0.6);
}

/* ───────── Top nav and the visitor (wide screens) ───────── */
.app-topnav { display: none; }
.app-top-me { display: none; align-items: center; gap: 12px; color: var(--text); text-decoration: none; }
.app-top-nick { font: 700 15px var(--font-display); }
.app-avatar {
    display: inline-grid; place-items: center; flex: none; width: 34px; height: 34px; border-radius: 50%;
    background: var(--surface-2); border: 2px solid var(--xp);
    font: 700 15px/1 var(--font-display); color: var(--xp-light); text-transform: uppercase;
}
/* A picked picture (app/users/avatars.js) in the same ring as the initial: blue, the
   colour of levels, since the picture is you as a player. */
img.app-avatar { box-sizing: border-box; object-fit: cover; }
.app-avatar.app-avatar-xl { width: 72px; height: 72px; font-size: 30px; }
.app-avatar.app-avatar-sm { width: 22px; height: 22px; border-width: 1.5px; font-size: 11px; }
/* Din grupp: nobody yet (a dashed empty ring) or a link sent (a ring with a dot). */
.app-holder-none, .app-holder-sent { flex: none; box-sizing: border-box; width: 22px; height: 22px; border-radius: 50%; }
.app-holder-none { border: 1.5px dashed var(--dim); }
.app-holder-sent { border: 1.5px solid var(--yellow); background: radial-gradient(circle, var(--yellow) 0 3px, transparent 3.5px); }
.app-visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.app-level { font: 700 10px var(--font-mono); letter-spacing: 0.12em; text-transform: uppercase; color: var(--xp); }
@media (min-width: 900px) {
    .app-tabs { display: none; }
    .app-shell-tabs { padding-bottom: 0; }
    .app-topnav { display: flex; align-self: stretch; gap: 28px; margin-right: auto; margin-left: 32px; }
    .app-topnav > a { position: relative; display: flex; align-items: center; font: 500 16px var(--font-body); color: rgba(237,233,228,0.7); text-decoration: none; }
    .app-topnav > a:hover { color: var(--text); }
    .app-topnav > a[aria-current] { color: var(--yellow); }
    .app-topnav > a[aria-current]::after {
        content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px; border-radius: 2px;
        background: var(--yellow); box-shadow: 0 0 10px rgba(255,186,83,0.6);
    }
    .app-top-me { display: inline-flex; }
}
.app-shell > .app-main { flex: 1; width: 100%; }
.app-main { max-width: 460px; margin: 0 auto; padding: 40px 16px 64px; }
.app-main-wide { max-width: 1000px; }

.app-foot {
    display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 6px 10px;
    padding: 24px 16px 32px; border-top: 1px solid var(--border-soft);
    font: 700 11px var(--font-mono); letter-spacing: 0.16em; text-transform: uppercase; color: var(--dim);
}
.app-foot img { height: 20px; width: auto; opacity: 0.8; }
.app-foot a { color: var(--mid); text-decoration: none; white-space: nowrap; }
.app-foot a:hover { color: var(--yellow); }

/* ───────── Type ───────── */
/* Eyebrow: <p class="app-eyebrow">Konto</p> above an h1 or h2. */
.app-eyebrow {
    display: flex; align-items: center; gap: 12px; margin: 0 0 12px;
    font: 700 11px var(--font-mono); letter-spacing: 0.3em; text-transform: uppercase; color: var(--yellow);
}
.app-eyebrow::before { content: ''; width: 28px; height: 1px; background: currentColor; flex-shrink: 0; }
.app-eyebrow.c-blue { color: var(--blue); }
.app-eyebrow.c-orange { color: var(--orange); }
.c-blue { color: var(--blue); }

h1 { font: 700 clamp(32px, 8vw, 44px)/1.05 var(--font-display); letter-spacing: -0.01em; margin: 0 0 14px; }
h2 { font: 700 22px/1.2 var(--font-display); margin: 0 0 12px; }
.app-section { margin-top: 44px; }
.app-lead { color: var(--mid); margin: 0 0 28px; }
.app-small { font-size: 14px; color: var(--mid); margin-top: 40px; }
.app-hint { font-size: 14px; color: var(--mid); margin: -4px 0 8px; }
.app-inline { margin-top: 20px; color: var(--mid); font-size: 14px; }
/* Big numbers (coins, XP, seat counts) use the label font. */
.app-stat { font: 700 28px/1 var(--font-mono); letter-spacing: 0.02em; color: var(--yellow); }

/* ───────── Cards ───────── */
.app-card {
    position: relative; background: var(--surface); border: 1px solid var(--border-soft); border-radius: 12px;
    padding: 20px; box-shadow: inset 0 1px 0 rgba(255,255,255,0.05);
}
/* A whole card that is a link: eyebrow, title, one line, arrow. Reused by the
   home tab later (next event, missions, news). */
.app-cards { display: flex; flex-direction: column; gap: 12px; margin: 0 0 12px; }
.app-card-link {
    display: block; color: var(--text); text-decoration: none;
    transition: border-color 0.18s ease, background 0.18s ease, transform 0.18s ease;
}
.app-card-link:hover { border-color: rgba(255,186,83,0.4); background: var(--surface-2); transform: translateY(-2px); }
.app-card-link .app-eyebrow { margin-bottom: 8px; }
.app-card-link h2 { margin: 0 0 4px; padding-right: 28px; }
.app-card-link p:not(.app-eyebrow) { margin: 0; color: var(--mid); font-size: 14px; }
.app-card-link::after {
    content: '→'; position: absolute; right: 20px; top: 50%; transform: translateY(-50%);
    font: 700 18px var(--font-mono); color: var(--yellow);
}

/* ───────── Forms and buttons ───────── */
.app-form { display: flex; flex-direction: column; gap: 10px; }
.app-form label:not(.app-check) {
    font: 700 11px var(--font-mono); letter-spacing: 0.16em; text-transform: uppercase; color: var(--mid);
}
/* One field style everywhere: 52px tall, darker than the card, a yellow ring on focus,
   orange when the server says the value is wrong (aria-invalid). */
.app-form input:not([type=checkbox]):not([type=hidden]), .app-field {
    height: 52px; padding: 0 14px; width: 100%;
    font: 500 16px var(--font-body); color: var(--text); background: var(--field);
    border: 1px solid var(--border); border-radius: 10px;
    transition: border-color 0.18s ease, box-shadow 0.18s ease;
}
.app-form input::placeholder, .app-field::placeholder { color: var(--dim); }
.app-form input:focus, .app-field:focus {
    outline: none; border-color: var(--yellow);
    box-shadow: inset 0 0 0 0.5px var(--yellow), 0 0 0 4px rgba(255,186,83,0.15);
}
.app-form input[aria-invalid=true], .app-field[aria-invalid=true] { border-color: var(--orange); box-shadow: inset 0 0 0 0.5px var(--orange); }
.app-form input[aria-invalid=true]:focus, .app-field[aria-invalid=true]:focus { box-shadow: inset 0 0 0 0.5px var(--orange), 0 0 0 4px rgba(255,104,66,0.15); }
/* A pasted list (the admin's crew emails): the field's look, as tall as it needs. */
textarea.app-field { height: auto; min-height: 8em; padding: 12px 14px; resize: vertical; font: 400 14px var(--font-mono); }
/* Checkboxes: a 24px box drawn here (not the browser's white one), a ✓ drawn with two
   borders when ticked; the whole row is the tap target. -danger ticks in orange. */
.app-check { display: flex; gap: 14px; align-items: center; min-height: 44px; cursor: pointer; }
.app-check input {
    appearance: none; -webkit-appearance: none; position: relative; flex: none; width: 24px; height: 24px; margin: 0;
    border: 1.5px solid var(--seat-free-stroke); border-radius: 6px; background: var(--field); cursor: pointer;
}
.app-check input:checked { background: var(--yellow); border-color: var(--yellow); }
.app-check input:checked::after {
    content: ''; position: absolute; left: 7px; top: 2px; width: 6px; height: 12px;
    border: solid var(--bg); border-width: 0 2.5px 2.5px 0; transform: rotate(45deg);
}
.app-check input:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }
.app-check-danger input:checked { background: var(--orange); border-color: var(--orange); }
/* A danger button behind a "Jag förstår…" box stays dim until the box is ticked
   (the server still checks the box). */
form:has(.app-check-danger input:not(:checked)) .app-btn-danger {
    background: transparent; color: rgba(255,104,66,0.55); border: 1px solid rgba(255,104,66,0.35); box-shadow: none; transform: none;
}

/* One button style everywhere: mono capitals, 54px tall (a comfortable thumb target),
   yellow with a glow; ghost and danger are the same shape. */
.app-btn {
    margin-top: 10px; display: inline-flex; justify-content: center; align-items: center; gap: 10px;
    min-height: 54px; padding: 0 28px;
    font: 700 14px/1 var(--font-mono); letter-spacing: 0.2em; text-transform: uppercase; text-align: center;
    color: var(--bg); background: var(--yellow); border: 0; border-radius: 8px; text-decoration: none;
    cursor: pointer; box-shadow: 0 0 12px rgba(255,186,83,0.22);
    transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease, color 0.18s ease;
}
.app-btn:hover { background: #ffc96b; box-shadow: 0 0 28px rgba(255,186,83,0.85), 0 0 60px rgba(255,186,83,0.3); transform: translateY(-2px); }
.app-btn:disabled { background: var(--surface-2); color: var(--dim); box-shadow: none; transform: none; cursor: not-allowed; }
.app-btn-danger { background: var(--orange); box-shadow: 0 0 12px rgba(255,104,66,0.22); }
.app-btn-danger:hover { background: #ff8262; box-shadow: 0 0 28px rgba(255,104,66,0.85), 0 0 60px rgba(255,104,66,0.3); }
.app-btn-ghost { background: transparent; color: var(--text); border: 1px solid var(--border); box-shadow: none; }
.app-btn-ghost:hover { background: transparent; color: var(--yellow); border-color: rgba(255,186,83,0.4); box-shadow: none; transform: none; }
.app-btn-sm { margin: 0; min-height: 0; padding: 9px 14px; font-size: 11px; letter-spacing: 0.12em; }
.app-link { font: inherit; color: var(--yellow); background: none; border: 0; padding: 0; cursor: pointer; text-decoration: underline; text-underline-offset: 3px; }
/* "Ge bort din biljett", "Ta bort länken": quieter, they take something away. */
.app-link.app-link-quiet { color: var(--mid); }

/* Messages: errors in orange, plain notices ("not open yet") in blue. */
.app-error, .app-notice { border: 1px solid; border-left-width: 3px; border-radius: 8px; padding: 10px 14px; margin: 0 0 16px; }
.app-error { background: rgba(255,104,66,0.10); border-color: rgba(255,104,66,0.5); border-left-color: var(--orange); }
.app-notice { background: rgba(36,155,215,0.10); border-color: rgba(36,155,215,0.45); border-left-color: var(--blue); }

/* Status pills: avbokad, avstängd, raderas. */
.app-tag {
    display: inline-block; padding: 2px 8px; border: 1px solid rgba(255,104,66,0.5); border-radius: 999px;
    font: 700 10px/1.6 var(--font-mono); letter-spacing: 0.12em; text-transform: uppercase; color: var(--orange);
    vertical-align: 2px;
}

/* ───────── Hem ───────── */
/* Micro-labels: "BEACON 2027 BÖRJAR OM", "ORDER 48213". */
.app-micro { margin: 0; font: 700 10px var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase; color: rgba(237,233,228,0.6); }
a.app-micro { text-decoration: none; color: var(--yellow); }
.c-orange { color: var(--orange); }
.c-dim { color: rgba(237,233,228,0.6); }

/* The visitor, on a phone (the top bar shows them on a wide screen). */
.app-me-head { display: flex; align-items: center; gap: 12px; margin: 0 0 16px; }
.app-me-head > span:last-child { display: flex; flex-direction: column; gap: 2px; }
.app-me-nick { font: 700 15px/1.2 var(--font-display); }
@media (min-width: 900px) { .app-me-head { display: none; } }

.app-home { display: flex; flex-direction: column; gap: 14px; }
.app-home-main, .app-home-side { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
@media (min-width: 900px) {
    .app-home { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); gap: 20px; align-items: start; }
    /* The next step stays in view while a long group list scrolls past beside it. */
    .app-home-main { position: sticky; top: 80px; }
}

/* The hero: the one thing that matters now. */
.app-hero {
    position: relative;
    background: var(--hero); border: 1px solid var(--border-soft); border-radius: 20px; padding: 18px;
    display: grid; gap: 20px;
}
.app-home .app-hero h2 { margin: 18px 0 8px; }
.app-home .app-hero-body > :first-child { margin-top: 0; padding-top: 0; border-top: 0; }
/* The countdown strip above the hero: the event and its dates, days to go. */
.app-countdown-strip {
    display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 14px;
    padding: 12px 16px; background: var(--surface); border: 1px solid var(--border-soft); border-radius: 12px;
}
.app-strip-dates { margin: 4px 0 0; font-size: 14px; color: var(--text); }
.app-countdown { display: flex; align-items: baseline; margin: 0; font: 700 24px/1 var(--font-mono); letter-spacing: -0.01em; white-space: nowrap; }
.app-countdown small { font-size: 11px; color: var(--dim); margin: 0 8px 0 2px; }
.app-countdown small:last-child { margin-right: 0; }
@media (min-width: 900px) {
    .app-countdown-strip { margin-bottom: 20px; }
    .app-countdown { font-size: 28px; }
}
.app-countdown-strip .app-live { margin: 0; }
.app-live {
    display: inline-flex; align-items: center; gap: 8px; margin: 12px 0 0;
    font: 700 13px var(--font-mono); letter-spacing: 0.16em; text-transform: uppercase; color: var(--orange);
}
.app-live::before { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--orange); box-shadow: var(--glow-o); }

/* The booker's steps (home.js heroView): done green, the current one yellow and lit. */
.app-steps { list-style: none; margin: 0; padding: 0; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 6px; }
.app-step { display: grid; gap: 7px; font: 700 9.5px/1.2 var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--dim); }
.app-step::before { content: ''; height: 4px; border-radius: 4px; background: rgba(255,255,255,0.12); }
.app-step-done { color: var(--green); }
.app-step-done::before { background: var(--green); }
.app-step-active { color: var(--yellow); }
.app-step-active::before { background: var(--yellow); box-shadow: 0 0 8px rgba(255,186,83,0.6); }
.app-hero-eyebrow { margin: 18px 0 0; font: 700 10px var(--font-mono); letter-spacing: 0.18em; text-transform: uppercase; color: var(--yellow); }
.app-home .app-hero .app-hero-title { margin: 8px 0 8px; font-size: 25px; line-height: 1.15; }
.app-hero-lead { color: var(--mid); font-size: 15px; margin: 0; }
/* A friend's hero: their seat badge, big, beside who the ticket is from. */
.app-hero-held { display: flex; align-items: center; gap: 16px; }
.app-hero-held .app-hero-title { margin-top: 0; }
.app-seat-badge.app-seat-badge-lg { flex: none; width: 72px; height: 72px; border-radius: 14px; font-size: 11px; }
.app-seat-badge.app-seat-badge-lg.app-seat-badge-booked { font-size: 24px; }
.app-seat-badge.app-seat-badge-lg.app-seat-badge-booked.app-seat-badge-pair { font-size: 14px; }
.app-hero-note { margin: 14px 0 0; font-size: 13.5px; color: var(--mid); }
/* A friend's one mission under the hero: pick a picture (dashed yellow, links to the picker). */
.app-mission {
    display: flex; align-items: center; gap: 12px; padding: 14px 16px; border: 1.5px dashed var(--accent-border);
    border-radius: 14px; background: var(--yellow-tint); color: var(--text); text-decoration: none;
}
.app-mission:hover { border-color: var(--yellow); }
.app-mission-text { flex: 1; display: flex; flex-direction: column; gap: 6px; }
.app-mission-text strong { font: 700 17px/1.2 var(--font-display); }
.app-mission-go { font: 700 24px/1 var(--font-mono); color: var(--yellow); }
/* The second half of a sentence only the wide hero has room for. */
.app-wide-only { display: none; }
/* The hero's one button: full width on a phone, its own width beside the map. */
.app-hero .app-btn { width: 100%; margin-top: 16px; }

/* The mini hall map: the seat page's map, read-only and small. */
.app-minimap { display: block; margin: 0; text-decoration: none; }
.app-minimap:hover .seatmap-svg { box-shadow: 0 0 0 1px var(--accent-border), 0 0 24px rgba(255,186,83,0.12); }
.app-minimap .seatmap { margin: 0 0 8px; pointer-events: none; }
.app-minimap .seatmap-svg { background: var(--bg); box-shadow: none; }
/* A glance on a phone, beside the text on a wide screen. */
@media (max-width: 899px) { .app-minimap .seatmap-svg { display: block; width: 100%; height: 170px; } }
.app-hero .app-form { margin-top: 16px; }
.app-minimap .seat-row-label, .app-minimap .seat-shape text { display: none; }
@media (min-width: 900px) {
    .app-hero { padding: 28px; }
    .app-hero-map { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; }
    .app-home .app-hero .app-hero-title { font-size: 32px; }
    .app-wide-only { display: inline; }
    .app-seat-badge.app-seat-badge-lg { width: 96px; height: 96px; border-radius: 16px; font-size: 12px; }
    .app-seat-badge.app-seat-badge-lg.app-seat-badge-booked { font-size: 30px; }
    .app-hero .app-btn { width: auto; margin-top: 24px; }
}

/* Panels and blocks under the hero. */
.app-panel { background: var(--surface); border-radius: 14px; padding: 18px; }
.app-panel-head, .app-block-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: 10px; }
.app-home :is(.app-panel-head, .app-block-head) h2 { margin: 0; font-size: 16px; }

.app-group-list { list-style: none; margin: 0; padding: 0; counter-reset: ticket; }
.app-group-list li {
    counter-increment: ticket; display: grid; grid-template-columns: 56px minmax(0, 1fr) auto; gap: 10px 14px;
    align-items: center; padding: 14px 0; border-top: 1px solid var(--border-soft);
}
/* The seat badge: the seat, or how urgent choosing one is (tickets.js badgeOf). */
.app-seat-badge {
    display: flex; align-items: center; justify-content: center; box-sizing: border-box;
    width: 56px; height: 56px; padding: 4px; border-radius: 12px; text-align: center;
    font: 700 9.5px/1.25 var(--font-mono); letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--mid); border: 1.5px dashed var(--dim);
}
.app-seat-badge-booked { font-size: 19px; line-height: 1; letter-spacing: 0; color: var(--green); background: var(--green-tint); border: 1.5px solid var(--green); }
.app-seat-badge-booked.app-seat-badge-pair { font-size: 12px; }
.app-seat-badge-warn { color: var(--orange); background: var(--orange-tint); border-color: var(--orange); }
.app-seat-badge-locked { color: var(--orange); background: rgba(255,104,66,0.12); border: 1.5px solid var(--orange); }
.app-seat-badge-later, .app-seat-badge-no_seat { color: var(--dim); border-style: solid; border-color: var(--border); }
.app-seat-badge-next { color: var(--yellow); background: var(--yellow-tint); border-color: var(--yellow); }
.app-ticket-body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.app-ticket-holder { display: flex; align-items: center; gap: 8px; margin: 0; font: 600 17px/1.2 var(--font-body); color: var(--text); overflow-wrap: anywhere; }
/* Nobody comes on it yet: the row is just "Biljett N", quieter than a name. */
.app-ticket-nobody { color: var(--mid); font-weight: 500; }
.app-ticket-nobody::before { content: 'Biljett ' counter(ticket); }
.app-ticket-meta { margin: 0; font-size: 13.5px; line-height: 1.3; color: var(--mid); }
.app-ticket-meta::before { content: 'Biljett ' counter(ticket) ' · '; }
.app-ticket-meta-bare::before { content: none; }
/* The row the next click on the map fills (seatmap.js). */
.app-group-list li.app-group-next { box-shadow: inset 3px 0 0 var(--yellow); padding-left: 12px; }
/* Giving a ticket away, and the link just made (holders.js, share.js). */
/* ⋮ at the end of a row (group-card): what can be done with the ticket, in a small menu. */
.app-ticket-menu { position: relative; align-self: center; }
.app-ticket-menu summary {
    display: flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 8px;
    list-style: none; cursor: pointer; color: var(--mid); font: 700 20px/1 var(--font-body); user-select: none;
}
.app-ticket-menu summary::-webkit-details-marker { display: none; }
.app-ticket-menu summary:hover, .app-ticket-menu[open] summary { color: var(--text); background: var(--surface-2); }
.app-ticket-menu summary:focus-visible { outline: 2px solid var(--yellow); outline-offset: 2px; }
.app-ticket-menu-list {
    position: absolute; right: 0; top: calc(100% + 4px); z-index: 20; min-width: 200px; padding: 6px;
    display: flex; flex-direction: column; background: var(--surface-2); border: 1px solid var(--border);
    border-radius: 10px; box-shadow: 0 12px 30px rgba(0,0,0,0.5);
}
.app-ticket-menu-list form { margin: 0; }
.app-ticket-menu-list button {
    width: 100%; padding: 10px 12px; border: 0; border-radius: 6px; background: none; text-align: left;
    color: var(--text); font: 500 14.5px var(--font-body); cursor: pointer; white-space: nowrap;
}
.app-ticket-menu-list button:hover, .app-ticket-menu-list button:focus-visible { background: rgba(255,255,255,0.06); outline: none; }
/* The link just made, shown once: amber and framed, important but not alarming. */
.app-gift-panel {
    position: relative; grid-column: 1 / -1; display: grid; gap: 12px; margin-top: 2px; padding: 16px;
    background: var(--hero); border: 1px solid var(--accent-border); border-radius: 12px;
    box-shadow: 0 0 24px rgba(255,186,83,0.08); font-size: 14px; line-height: 1.45; color: var(--mid);
}
.app-gift-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
.app-gift-eyebrow { font: 700 10px/1 var(--font-mono); letter-spacing: 0.18em; text-transform: uppercase; color: var(--yellow); }
.app-gift-chip { padding: 4px 7px; border: 1px solid var(--border); border-radius: 999px; font: 700 9.5px/1 var(--font-mono); letter-spacing: 0.12em; text-transform: uppercase; color: var(--mid); }
.app-gift-url {
    box-sizing: border-box; width: 100%; min-width: 0; height: 44px; padding: 0 12px;
    font: 700 12.5px var(--font-mono); color: var(--text); background: var(--field);
    border: 1px solid var(--border); border-radius: 8px; text-overflow: ellipsis;
}
/* Two columns; "Dela" is hidden where there's no share sheet, and "Kopiera" fills the row. */
.app-gift-actions { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 8px; }
.app-gift-actions .app-btn { padding: 0 12px; }
.app-gift-actions .app-btn[hidden] { display: none; }
/* Din biljett: a ticket a friend gave (held-card). */
.app-held-type { margin: 0 0 6px; font-size: 15px; }
.app-held-seat { margin: 0 0 6px; }
.app-held-seat strong { font: 700 22px var(--font-mono); color: var(--green); }

.app-highlights { list-style: none; margin: 0; padding: 0; }
.app-panel-head a { white-space: nowrap; }
.app-highlights li { display: grid; grid-template-columns: 76px 1fr auto; align-items: baseline; gap: 12px; padding: 6px 0; }
.app-highlights a { color: var(--text); text-decoration: none; }
.app-highlights a:hover { color: var(--yellow); }
/* The favourite star: a 44px target (favourites aren't built yet, so it's greyed),
   pulled into the row's height so the rows don't grow. */
.app-star { display: inline-grid; place-items: center; width: 44px; height: 44px; margin: -12px -10px -12px 0; font-size: 22px; line-height: 1; color: var(--yellow); }

/* One ticket per order: the order on top, a perforated line, then the stub
   with the order's one next step (stage from app/tickets/home.js). */
.app-ticket {
    --notch: 9px;
    position: relative; background: var(--surface); border: 1px solid var(--border-soft); border-radius: 12px;
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.05);
}
.app-ticket-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; padding: 18px 20px 0; }
.app-ticket h2 { margin: 0; font-size: 20px; }
.app-order-no { margin: 2px 0 0; font: 700 12px var(--font-mono); letter-spacing: 0.08em; color: var(--dim); }
.app-ticket-types { margin: 10px 0 0; padding: 0 20px 18px; color: var(--mid); font-size: 15px; }
.app-ticket-stub { position: relative; padding: 18px 20px 20px; border-top: 1px dashed var(--border); }
/* The notches: page-coloured circles over the card's edge at the perforation. */
.app-ticket-stub::before, .app-ticket-stub::after {
    content: ''; position: absolute; top: calc(var(--notch) * -1 - 1px); width: calc(var(--notch) * 2); height: calc(var(--notch) * 2);
    border-radius: 50%; background: var(--bg); border: 1px solid var(--border-soft);
}
.app-ticket-stub::before { left: calc(var(--notch) * -1 - 1px); clip-path: inset(0 0 0 50%); }
.app-ticket-stub::after { right: calc(var(--notch) * -1 - 1px); clip-path: inset(0 50% 0 0); }
.app-ticket-stub p { margin: 0 0 6px; }
.app-ticket-stub .app-btn { margin-top: 12px; width: 100%; }
.app-ticket-label { font: 700 11px var(--font-mono) !important; letter-spacing: 0.2em; text-transform: uppercase; color: var(--yellow); margin-bottom: 8px !important; }
.app-seats { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 6px !important; }
.app-seat-label {
    font: 700 24px/1 var(--font-mono); color: var(--text); padding: 10px 14px; border-radius: 8px;
    background: var(--bg-2); border: 1px solid rgba(255,186,83,0.45); box-shadow: 0 0 18px rgba(255,186,83,0.12);
}
.app-stage-choose { border-color: rgba(255,186,83,0.35); }
.app-stage-cancelled { opacity: 0.6; }
.app-stage-cancelled .app-ticket-label, .app-stage-no_seats .app-ticket-label { color: var(--mid); }

/* "Koppla en order till": closed until asked for (or an error to show). */
.app-more { margin-top: 16px; }
.app-more summary {
    cursor: pointer; width: max-content; color: var(--yellow);
    font: 700 12px var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase;
}
.app-more summary::marker { color: var(--dim); }
.app-more[open] summary { margin-bottom: 14px; }
.app-tag-blue { color: var(--blue); border-color: rgba(36,155,215,0.5); }

.c-yellow { color: var(--yellow); }

/* Corner brackets, as on the home page (home5.css .h5-hero): a 2px neon L, blue top
   left and yellow bottom right, that flickers on like a tube when the page opens.
   In the app they frame a moment only, the result pages (after a scan, a ticket
   received), as the home page frames whole sections; a page's main card has the
   top-edge signal instead (.app-signal).
   On a card they stand a little outside it and bend with its corner (the card's
   radius plus the gap), so they frame it instead of doubling its border.
   --bracket-r is the card's own radius. */
.app-bracket { position: relative; --bracket-r: 14px; --bracket-gap: 6px; }
.app-bracket::before, .app-bracket::after {
    content: ''; position: absolute; width: 36px; height: 36px; pointer-events: none; box-sizing: border-box;
    border: 0 solid var(--bracket);
    filter: drop-shadow(0 0 5px var(--bracket)) drop-shadow(0 0 2px var(--bracket));
    animation: app-neon-on 0.9s steps(1, end) 0.15s both;
}
.app-bracket::before {
    --bracket: rgba(36,155,215,0.75); top: calc(-1 * var(--bracket-gap)); left: calc(-1 * var(--bracket-gap));
    border-top-width: 2px; border-left-width: 2px; border-top-left-radius: calc(var(--bracket-r) + var(--bracket-gap));
}
.app-bracket::after {
    --bracket: rgba(255,186,83,0.7); bottom: calc(-1 * var(--bracket-gap)); right: calc(-1 * var(--bracket-gap));
    border-bottom-width: 2px; border-right-width: 2px; border-bottom-right-radius: calc(var(--bracket-r) + var(--bracket-gap));
    animation-delay: 0.4s;
}
@keyframes app-neon-on {
    0% { opacity: 0; } 15% { opacity: 0.9; } 25% { opacity: 0.1; } 40% { opacity: 0.8; }
    50% { opacity: 0.2; } 65% { opacity: 0.9; } 100% { opacity: 0.9; }
}
@media (prefers-reduced-motion: reduce) { .app-bracket::before, .app-bracket::after { animation: none; opacity: 0.9; } }

/* Chips: switch between orders on Biljett, jump to a day on Schema. */
.app-chips { display: flex; gap: 8px; overflow-x: auto; margin: 0 -16px 16px; padding: 2px 16px 4px; scrollbar-width: none; }
.app-chip {
    flex: none; padding: 7px 14px; border: 1px solid var(--border); border-radius: 999px;
    font: 700 11px var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase;
    color: var(--mid); text-decoration: none; white-space: nowrap;
}
.app-chip:hover { color: var(--yellow); border-color: var(--accent-border); }
.app-chip[aria-current] { color: var(--bg); background: var(--yellow); border-color: var(--yellow); }
.app-chip-add { color: var(--yellow); border-style: dashed; border-color: var(--accent-border); }

/* Biljett: the hall map with "Boka" under it, and the order and its group in a
   column beside it on a wide screen, under it on a phone. A refused booking's
   reason is on top of the map. */
.app-booking { display: flex; flex-direction: column; gap: 14px; }
.app-booking-map, .app-booking-side { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.app-mapcard { position: relative; background: var(--surface); border: 1px solid var(--border-soft); border-radius: 14px; padding: 12px; }
.app-mapcard .seatmap { margin: 0; }
.app-mapcard .seatmap-svg { background: var(--bg); border: 0; box-shadow: none; }
.app-booking-map .seat-legend { margin: 0; }
.app-mapcard .seatmap-caption { margin-bottom: 0; }
.app-booking-status { margin: 0 0 8px; color: rgba(237,233,228,0.82); font-size: 15px; }
.app-booking-side .app-panel-head h2 { margin: 0; font-size: 16px; }
/* A refused booking, or why a click on the map picked nothing: under the top bar,
   in the middle, gone after about four seconds (seatmap.js removes it then); an
   error that needs reading, six. */
.app-toasts {
    position: fixed; top: 76px; left: 50%; translate: -50% 0; z-index: 50; width: max-content;
    max-width: calc(100vw - 32px); display: flex; flex-direction: column; align-items: center; gap: 8px; pointer-events: none;
}
.app-toast {
    margin: 0; max-width: 440px; padding: 12px 16px; font-size: 15px; color: var(--text);
    background: rgba(42,16,14,0.96); border: 1px solid rgba(255,104,66,0.55); border-left: 3px solid var(--orange); border-radius: 10px;
    box-shadow: 0 12px 40px rgba(0,0,0,0.5), 0 0 30px rgba(255,104,66,0.22);
    animation: app-toast 4s ease forwards;
}
.app-toast-long { animation-duration: 6s; }
@keyframes app-toast {
    from { opacity: 0; translate: 0 -12px; }
    8%, 85% { opacity: 1; translate: 0 0; }
    to { opacity: 0; translate: 0 -12px; visibility: hidden; }
}
@media (prefers-reduced-motion: reduce) {
    @keyframes app-toast { from { opacity: 0; } 8%, 85% { opacity: 1; } to { opacity: 0; visibility: hidden; } }
}
.app-booking-form { margin: 0; }
.app-booking-form .app-hint { margin: 0; }
.app-booking-form .app-btn { width: 100%; }
/* Once there is something to book, the form sticks to the bottom of the screen (above
   the tab bar on a phone) while the map is in view, so "Boka" is never a scroll away. */
.app-booking-form:has(.app-btn:enabled:not([hidden])) {
    position: sticky; bottom: calc(80px + env(safe-area-inset-bottom)); z-index: 5; padding: 12px;
    background: rgba(17,23,39,0.96); border: 1px solid var(--accent-border); border-radius: 14px;
    box-shadow: 0 -8px 30px rgba(0,0,0,0.45);
}
@media (min-width: 900px) {
    .app-booking-form:has(.app-btn:enabled:not([hidden])) { bottom: 16px; }
}
/* A wide screen: the map on the left, the column beside it stays in view while the page scrolls. */
@media (min-width: 900px) {
    .app-main-wide:has(.app-booking), body:has(.app-booking) .app-top-inner { max-width: 1240px; }
    .app-booking { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 14px 20px; align-items: start; }
    /* A big group can be taller than the screen: then the column scrolls on its own, so "Boka" is never cut off. */
    .app-booking-side { position: sticky; top: 80px; max-height: calc(100vh - 96px); overflow-y: auto; }
}
.app-showname { margin: 0; }

/* Schema: the programme day by day. */
.app-schedule { display: flex; flex-direction: column; gap: 14px; margin: 0 0 16px; }
.app-schedule .app-panel { scroll-margin-top: 80px; }
.app-schedule .app-panel-head h2 { margin: 0; font-size: 18px; }
.app-schedule-list { list-style: none; margin: 8px 0 0; padding: 0; }
.app-schedule-list li { display: grid; grid-template-columns: 92px 1fr auto; align-items: baseline; gap: 12px; padding: 10px 0; border-top: 1px solid rgba(237,233,228,0.08); }
.app-schedule-time { font: 700 13px var(--font-mono); color: var(--yellow); white-space: nowrap; }
.app-schedule-what { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.app-schedule-what strong { font-weight: 600; }

/* "Boka platserna" under the map: stays in reach while the page scrolls. */

/* ───────── Admin ───────── */
.app-orders { list-style: none; padding: 0; margin: 0 0 12px; display: flex; flex-direction: column; gap: 10px; }
.app-orders li { background: var(--surface); border: 1px solid var(--border-soft); border-radius: 12px; padding: 12px 16px; }
/* The admin: a sidebar by job, the page beside it; on a phone the sidebar is a row on top. */
.admin { display: grid; grid-template-columns: 180px minmax(0, 1fr); gap: 0 32px; max-width: 1240px; width: 100%; margin: 0 auto; padding: 0 16px; flex: 1; }
.admin > .app-main { margin: 0; padding-left: 0; padding-right: 0; max-width: none; }
.admin-side { position: sticky; top: 64px; align-self: start; display: flex; flex-direction: column; gap: 2px; padding: 40px 0 24px; }
.admin-side a {
    font: 700 11px var(--font-mono); letter-spacing: 0.16em; text-transform: uppercase;
    color: var(--mid); text-decoration: none; padding: 9px 12px; border-left: 2px solid transparent; border-radius: 0 6px 6px 0;
}
.admin-side a:hover { color: var(--yellow); background: rgba(255,255,255,0.03); }
.admin-side a[aria-current] { color: var(--yellow); border-left-color: var(--yellow); background: rgba(255,186,83,0.07); }
.admin-side .admin-side-out { margin-top: 20px; color: var(--dim); }
@media (max-width: 760px) {
    .admin { grid-template-columns: minmax(0, 1fr); }
    .admin-side { position: static; flex-direction: row; flex-wrap: wrap; padding: 16px 0 0; border-bottom: 1px solid var(--border-soft); }
    .admin-side a { border-left: 0; border-bottom: 2px solid transparent; border-radius: 0; }
    .admin-side a[aria-current] { border-bottom-color: var(--yellow); }
    .admin-side .admin-side-out { margin-top: 0; }
    .admin > .app-main { padding-top: 24px; }
}
.app-main-wide h2 { margin-top: 36px; }
.app-search input { font: inherit; color: var(--text); background: var(--bg-2); border: 1px solid var(--border); border-radius: 8px; padding: 9px 12px; min-width: 0; width: 100%; }
.app-search input:focus { outline: none; border-color: var(--yellow); box-shadow: 0 0 0 3px rgba(255,186,83,0.18); }
.app-top-search { margin: 0; width: min(320px, 46vw); }
.app-top-search input { font-size: 14px; }

/* "Farliga åtgärder": ban, delete, cancel, unclaim. Last on the page, closed until asked for. */
.app-danger { margin-top: 48px; border: 1px solid rgba(255,104,66,0.3); border-radius: 12px; padding: 14px 20px; }
.app-danger summary {
    cursor: pointer; color: var(--orange);
    font: 700 12px var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase;
}
.app-danger[open] { padding-bottom: 24px; }
.app-danger h3 { font: 700 18px/1.2 var(--font-display); margin: 24px 0 8px; }
.app-danger .app-form { max-width: 460px; }
.app-top-wide { max-width: 1240px; }
/* On a phone the admin's search gets its own row under the logo and links, so they stay on screen. */
@media (max-width: 599px) {
    .app-top-wide { flex-wrap: wrap; height: auto; padding-top: 12px; padding-bottom: 12px; row-gap: 10px; }
    .app-top-wide .app-top-actions { flex: 1; flex-wrap: wrap; justify-content: flex-end; row-gap: 10px; }
    .app-top-wide .app-top-search { order: 1; width: 100%; }
}
.app-facts dd { min-width: 0; }
.app-facts dd .app-row-form { display: flex; max-width: 18em; }
.app-facts dd .app-row-form input { flex: 1; min-width: 0; width: auto; }
.app-facts dd .app-hint { margin: 0 0 0 8px; }
.app-table { display: block; overflow-x: auto; border-collapse: collapse; font-size: 14px; }
.app-table th, .app-table td { text-align: left; padding: 10px 14px 10px 0; border-bottom: 1px solid var(--border-soft); vertical-align: top; }
.app-table th { font: 700 10px var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--dim); white-space: nowrap; }
.app-table tbody tr:hover td { background: rgba(255,255,255,0.02); }
.app-table code { font-size: 13px; color: var(--mid); word-break: break-all; }
.app-facts { display: grid; grid-template-columns: max-content 1fr; gap: 6px 16px; margin: 0; }
.app-facts dt { color: var(--mid); }
.app-facts dd { margin: 0; }

/* Seat map (public/app/seatmap.js). The SVG scales to the page width. */
.seatmap { margin: 8px 0 12px; }
.seatmap-svg { width: 100%; height: auto; background: var(--surface); border: 1px solid var(--border-soft); border-radius: 12px; box-shadow: 0 0 40px rgba(36,155,215,0.08); }
.seat-shape polygon { fill: var(--border); fill-opacity: 0.5; stroke: var(--mid); stroke-opacity: 0.6; }
.seat-shape text { font: 600 16px var(--font-body); fill: var(--mid); pointer-events: none; }
.seat-row-label { font: 700 22px var(--font-mono); fill: var(--mid); pointer-events: none; }
.seat { stroke-width: 2; }
/* Each state has its own shape, not only its own colour (the legend under the map):
   free = outline, yours = solid green, new pick = solid yellow and lit, leaving =
   dashed green outline, taken = solid grey, not for your tickets = hatched and dim. */
.seat-free { fill: rgba(127,147,176,0.10); stroke: var(--seat-free-stroke); stroke-width: 1.5; cursor: pointer; }
.seat-picked { fill: var(--yellow); stroke: none; filter: drop-shadow(0 0 4px rgba(255,186,83,0.75)); cursor: pointer; }
/* The booker's own seats while changing: still booked, and booked but clicked away. */
.seat-booked { fill: var(--green); stroke: none; cursor: pointer; }
.seat-leaving { fill: transparent; stroke: var(--green); stroke-dasharray: 3 2; cursor: pointer; }
.seat-leaving:hover { stroke: var(--yellow); }
.seat-taken, .seat-blocked { fill: var(--seat-taken); stroke: none; }
/* The hatch is a <pattern> seatmap.js puts in the first map's <defs>. */
.seat-closed { fill: url(#seat-hatch); stroke: rgba(255,255,255,0.07); stroke-width: 1; }
/* A friend's map: their own seat (ringed and lit) and the rest of their group. */
.seat-own { fill: var(--green); stroke: var(--green); stroke-width: 4; paint-order: stroke; filter: drop-shadow(0 0 5px rgba(124,224,165,0.6)); }
.seat-group { fill: rgba(124,224,165,0.28); stroke: var(--green); stroke-width: 1.5; }
/* A seat Beacon placed one of the order's tickets on: theirs, but not to click. */
.seat-kept { fill: var(--green); fill-opacity: 0.55; stroke: none; }
.seat[tabindex]:focus { outline: none; stroke: var(--yellow); stroke-width: 4; }
.seat-free:hover { stroke: var(--yellow); }
/* The admin's map (seats page): held for admins, blocked, and the seats picked for the lock form. */
.seat-held { fill: rgba(36,155,215,0.16); stroke: var(--blue); stroke-width: 1.5; stroke-dasharray: 3 2; }
.app-admin-map .seat-blocked { stroke: rgba(255,255,255,0.4); stroke-width: 1.5; }
.app-admin-map .seat-free, .app-admin-map .seat-held, .app-admin-map .seat-blocked { cursor: pointer; }
.seat.seat-selected { stroke: var(--yellow); stroke-width: 3; stroke-dasharray: none; }
.seatmap-svg a .seat { cursor: pointer; }
.seatmap-svg a:hover .seat { stroke: var(--yellow); }
/* Elite: a picked table side is one wide seat over its two; the partner a click would
   take lights up; a seat whose table side isn't free fades. The lines mark where tables meet. */
.seat-wide { fill: var(--yellow); stroke: var(--text); stroke-width: 2; pointer-events: none; }
.seat-wide-booked { fill: var(--green); }
.seat-hover { stroke: var(--yellow); }
.seat-lone { fill-opacity: 0.4; stroke-opacity: 0.4; }
.seat-dividers line { stroke: var(--mid); stroke-opacity: 0.45; stroke-width: 1.5; pointer-events: none; }
/* Above the map while booking: which kind of ticket the next click is for, and the switch. */
.seatmap-mode { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin: 0 0 10px; }
/* The stepper (seatmap.js stepper): how many are done, and which ticket the next click is for. */
.seatmap-stepper {
    flex: 1 1 100%; display: grid; gap: 10px; padding: 14px;
    background: var(--surface-2); border: 1px solid var(--accent-border); border-radius: 14px;
}
.seatmap-stepper-head { display: flex; justify-content: space-between; gap: 8px; margin: 0; }
.seatmap-stepper-eyebrow, .seatmap-stepper-count { font: 700 10px/1 var(--font-mono); letter-spacing: 0.16em; text-transform: uppercase; }
.seatmap-stepper-eyebrow { color: var(--yellow); }
.seatmap-stepper-done { color: var(--green); }
.seatmap-stepper-count { color: var(--mid); }
.seatmap-stepper-task { margin: 0; font: 600 18px/1.3 var(--font-body); color: var(--text); }
.seatmap-stepper-task span { color: var(--mid); font-weight: 500; }
/* With nothing to change: a green note in place of the button. */
.seatmap-done {
    margin: 0 0 12px; padding: 12px 14px; border-radius: 10px; background: var(--green-tint);
    border: 1px solid var(--green-line); color: var(--green); font-size: 14px; line-height: 1.4;
}
.seatmap-done[hidden], .app-booking-form .app-btn[hidden] { display: none; }
.seatmap-mode-btns { display: flex; gap: 8px; }
.seatmap-mode-btn {
    font: 700 12px var(--font-mono); letter-spacing: 0.04em; color: var(--text); background: var(--surface-2);
    border: 1px solid var(--border); border-radius: 8px; padding: 9px 12px; cursor: pointer;
}
.seatmap-mode-btn[aria-pressed=true] { color: var(--bg); background: var(--yellow); border-color: var(--yellow); box-shadow: 0 0 0 3px rgba(255,186,83,0.25); }
/* Above the map: "Hitta en vän" and zoom; under it, whose seat was tapped. */
.seatmap-tools { display: flex; gap: 8px; margin: 0 0 10px; }
/* "Hitta en vän" is an .app-field, as tall as the zoom buttons beside it. */
.seatmap-tools .app-field { flex: 1; min-width: 0; width: auto; height: 44px; font-size: 15px; }
.seatmap-tools .seatmap-zoom:first-of-type { margin-left: auto; }
.seatmap-zoom {
    flex: none; width: 44px; height: 44px; border-radius: 8px; border: 1px solid var(--border);
    background: var(--surface-2); color: var(--text); font: 700 22px/1 var(--font-mono); cursor: pointer;
}
.seatmap-zoom:hover { color: var(--yellow); border-color: var(--accent-border); }
/* The map scrolls inside its box when zoomed in; on a phone, about two thirds of the screen at most. */
.seatmap-scroll { position: relative; overflow: auto; max-height: 70vh; border-radius: 10px; overscroll-behavior: contain; }
.seatmap-scroll .seatmap-svg { display: block; max-width: none; }
/* At the widest the whole hall fits the box's height too (the SVG keeps its shape inside). */
.seatmap-scroll:not(.seatmap-zoomed) .seatmap-svg { max-height: 70vh; }
.seatmap-zoomed { cursor: grab; }
.seatmap-dragging, .seatmap-dragging .seat { cursor: grabbing; user-select: none; }
.seatmap-caption { min-height: 1.6em; margin: 8px 0 0; font: 700 12px var(--font-mono); letter-spacing: 0.06em; color: var(--yellow); }
/* The group over the map (seatmap.js placeGroup): HTML in the map's scrolled content,
   so it keeps its size at any zoom. A green ring that pulses once. */
.seatmap-overlay { position: absolute; left: 0; top: 0; width: 0; height: 0; pointer-events: none; }
.seatmap-ring {
    position: absolute; box-sizing: border-box; border: 2px solid var(--green); border-radius: 10px;
    box-shadow: var(--glow-g), inset 0 0 10px rgba(124,224,165,0.15); animation: seatmap-ring-in 900ms ease-out 1;
}
@keyframes seatmap-ring-in { 0% { transform: scale(1.6); opacity: 0; } 60% { opacity: 1; } 100% { transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .seatmap-ring { animation: none; } }
/* After three seconds (seatmap.js) the ring fades out; pointing at a group seat, or zooming in, brings it back. */
.seatmap-ring { transition: opacity 300ms ease; }
.seatmap-group-quiet:not(.seatmap-group-hover):not(.seatmap-group-zoomed) .seatmap-ring { opacity: 0; }
/* Zoomed in, a name tag over each group seat; --stem (seatmap.js) reaches down to it. */
.seatmap-tag {
    position: absolute; transform: translateX(-50%); height: 24px; padding: 0 8px; box-sizing: border-box;
    display: none; align-items: center; gap: 6px; white-space: nowrap;
    background: rgba(17,23,39,0.96); border: 1px solid var(--green); border-radius: 999px;
    font: 700 12px/1 var(--font-body); color: var(--text); box-shadow: 0 4px 12px rgba(0,0,0,0.5);
}
.seatmap-tags-on .seatmap-tag { display: flex; }
.seatmap-tag::after { content: ''; position: absolute; left: 50%; top: 100%; width: 1px; height: var(--stem, 6px); background: var(--green); }
.seatmap-tag small { font: 700 10px/1 var(--font-mono); color: var(--dim); }
/* "Zooma till oss" / "Visa hela hallen", over the map's bottom left corner. */
.seatmap-frame { position: relative; }
.seatmap-pill {
    position: absolute; left: 10px; bottom: 10px; height: 36px; padding: 0 14px; display: inline-flex; align-items: center;
    border-radius: 999px; background: rgba(17,23,39,0.96); border: 1px solid var(--green); color: var(--green);
    font: 700 11px/1 var(--font-mono); letter-spacing: 0.12em; text-transform: uppercase; cursor: pointer;
}
.seatmap-pill-neutral { border-color: rgba(255,255,255,0.2); color: var(--text); }
.seatmap-pill:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
/* The name card: whose a tapped seat is, inside the map at the bottom (seatmap.js showCard). */
.seatmap-card {
    position: absolute; left: 10px; right: 10px; bottom: 10px; display: flex; align-items: center; gap: 12px;
    padding: 12px 6px 12px 14px; background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
    box-shadow: 0 10px 30px rgba(0,0,0,0.6);
}
.seatmap-card[hidden] { display: none; }
.seatmap-card-ours { border-color: var(--green-line); }
.seatmap-card .app-avatar { width: 44px; height: 44px; font-size: 19px; }
.seatmap-card-body { flex: 1; min-width: 0; }
.seatmap-card-head { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; margin: 0; }
.seatmap-card-head strong { font: 700 19px/1.2 var(--font-display); color: var(--text); overflow-wrap: anywhere; }
.seatmap-card-tag {
    padding: 4px 8px; border-radius: 999px; background: var(--green-tint); border: 1px solid var(--green-line);
    font: 700 9.5px/1 var(--font-mono); letter-spacing: 0.16em; text-transform: uppercase; color: var(--green);
}
.seatmap-card-line { margin: 4px 0 0; font-size: 13.5px; line-height: 1.3; color: var(--mid); }
.seatmap-card-close {
    flex: none; width: 44px; height: 44px; border: 0; background: none; color: var(--mid);
    font: 400 26px/1 var(--font-body); cursor: pointer; border-radius: 8px;
}
.seatmap-card-close:hover { color: var(--text); }
.seatmap-card-close:focus-visible { outline: 2px solid var(--blue); }
.seat[role=button] { cursor: pointer; }
.seat-friend { stroke: var(--blue); stroke-width: 4; filter: drop-shadow(0 0 6px var(--blue)); }
/* The legend: 16px swatches drawn like the seats, two columns on a phone, one row on a wide screen. */
.seat-legend {
    list-style: none; padding: 0; margin: 0 0 20px; display: grid; grid-template-columns: 1fr 1fr; gap: 10px 16px;
    font: 700 10.5px/1.2 var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--mid);
}
.seat-legend li { display: flex; align-items: center; gap: 10px; }
@media (min-width: 900px) { .seat-legend { display: flex; flex-wrap: wrap; gap: 12px 28px; } }
.seat-dot { display: inline-block; flex: none; box-sizing: border-box; width: 16px; height: 16px; border-radius: 4px; }
.seat-dot.seat-free { border: 1.5px solid var(--seat-free-stroke); background: rgba(127,147,176,0.1); }
.seat-dot.seat-picked { background: var(--yellow); box-shadow: 0 0 8px rgba(255,186,83,0.7); }
.seat-dot.seat-booked { background: var(--green); }
.seat-dot.seat-leaving { border: 2px dashed var(--green); }
.seat-dot.seat-taken { background: var(--seat-taken); }
.seat-dot.seat-closed { border: 1px solid rgba(255,255,255,0.1); background: repeating-linear-gradient(45deg, rgba(255,255,255,0.16) 0 1px, transparent 1px 4px); }
.seat-dot.seat-kept { background: var(--green); opacity: 0.55; }
.seat-dot.seat-held { border: 1.5px dashed var(--blue); background: rgba(36,155,215,0.16); }
.seat-dot.seat-blocked { background: var(--seat-taken); border: 1.5px solid rgba(255,255,255,0.4); }
.seat-dot.seat-selected { border: 3px solid var(--yellow); }
.app-lock-form { display: grid; gap: 8px; margin: 12px 0 24px; max-width: 640px; }
.app-lock-buttons { display: flex; flex-wrap: wrap; gap: 8px; }
.app-link-btn { background: none; border: 0; padding: 0; color: var(--blue); cursor: pointer; font: inherit; text-decoration: underline; }
.seat-dot.seat-own { background: var(--green); box-shadow: 0 0 0 2px var(--bg), 0 0 0 3.5px var(--green); }
.seat-dot.seat-group { background: rgba(124,224,165,0.28); border: 1.5px solid var(--green); }

/* Small forms inside an admin table row (move a seat, cancel a ticket). */
.app-row-actions { display: flex; flex-wrap: wrap; gap: 6px; }
.app-row-form { display: flex; gap: 6px; margin: 0; }
.app-row-form input { width: 5.5em; font: inherit; color: var(--text); background: var(--bg-2); border: 1px solid var(--border); border-radius: 6px; padding: 4px 8px; }
.app-row-form .app-btn { margin: 0; min-height: 0; padding: 6px 10px; font-size: 11px; letter-spacing: 0.12em; box-shadow: none; }
.app-row-form input.app-input-wide { width: 16em; max-width: 100%; }
.app-adjust { flex-wrap: wrap; margin: 12px 0 16px; }
.app-adjust input[name="note"] { flex: 1 1 16em; width: auto; }

@media (prefers-reduced-motion: reduce) {
    .app-btn, .app-card-link, .app-form input { transition: none; }
    .app-btn:hover, .app-card-link:hover { transform: none; }
}

/* The 6-digit code field: big and spaced out, like on the ticket. */
.app-form input.app-code-input:not([type=checkbox]) { height: 60px; font: 700 26px var(--font-mono); letter-spacing: 0.35em; text-align: center; }

/* ───────── Level, XP and coins (Profil) ───────── */
a.app-level { display: block; text-decoration: none; }
.app-progress-card { background: var(--surface); border-radius: 14px; padding: 18px; }
.app-progress-title { margin: 4px 0 12px; font: 700 26px/1.1 var(--font-display); text-transform: uppercase; color: var(--xp); }
.app-progress { display: block; width: 100%; height: 8px; appearance: none; border: 0; border-radius: 4px; background: var(--surface-2); overflow: hidden; }
.app-progress::-webkit-progress-bar { background: var(--surface-2); }
.app-progress::-webkit-progress-value { background: linear-gradient(90deg, #1f7fb4, var(--xp)); border-radius: 4px; }
.app-progress::-moz-progress-bar { background: linear-gradient(90deg, #1f7fb4, var(--xp)); border-radius: 4px; }
.app-small.app-tight { margin-top: 8px; }
.app-coins-stat { margin: 24px 0 0; color: var(--yellow); }
.app-coins-stat small { font-size: 13px; color: var(--mid); }
.app-history { list-style: none; margin: 0; padding: 0; }
.app-history li { display: flex; gap: 14px; padding: 10px 0; border-bottom: 1px solid var(--border); }
.app-history-amount { flex: none; min-width: 7em; font: 700 13px var(--font-mono); color: var(--yellow); }
.app-history-amount.c-blue { color: var(--blue); }

/* ───────── Achievements (Profil, and after a claim) ───────── */
.app-badges { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.app-badge { display: flex; align-items: center; gap: 12px; padding: 12px; border: 1px solid var(--border-soft); border-radius: 12px; opacity: 0.6; }
.app-badge.is-earned { opacity: 1; background: var(--surface); border-color: rgba(76,182,236,0.3); }
.app-badge > div, .app-unlocked > div { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.app-badge .app-progress { margin-top: 8px; height: 6px; }
.app-badge-icon { flex: none; display: grid; place-items: center; width: 40px; height: 40px; border-radius: 10px; background: var(--surface-2); font-size: 22px; }
.app-badge-reward { flex: none; font: 700 12px var(--font-mono); color: var(--mid); text-align: right; }
/* XP blue, coins yellow; still to earn, they dim with the rest of the badge. */
.app-badge-reward .c-xp { color: var(--xp); }
.app-badge-reward .c-coins { color: var(--yellow); }
.app-unlocked { display: flex; align-items: center; gap: 12px; margin: 0 0 16px; padding: 12px; text-align: left; background: var(--surface); border: 1px solid rgba(76,182,236,0.4); border-radius: 12px; }

/* ───────── Stations and claims ───────── */
.app-station { text-align: center; max-width: 560px; margin: 0 auto; }
.app-station h1 { margin: 6px 0; }
.app-station-coins { font: 700 40px/1 var(--font-mono); color: var(--yellow); margin: 8px 0 20px; }
.app-station-qr { background: #fff; border-radius: 16px; padding: 12px; width: min(80vw, 60vh, 460px); margin: 0 auto; }
.app-station-qr svg { display: block; width: 100%; height: auto; }
/* Under the QR: a blue bar that runs down to the next code (station.js sets its length). */
.app-station-timer { width: min(80vw, 60vh, 460px); height: 4px; margin: 16px auto 0; border-radius: 4px; background: rgba(255,255,255,0.1); overflow: hidden; }
.app-station-timer span { display: block; height: 100%; background: var(--blue); box-shadow: var(--glow-b); transform-origin: left; animation: app-code-timer 30s linear forwards; }
.app-station-screen .app-station-timer span { background: linear-gradient(90deg, var(--orange), var(--yellow) 50%, var(--blue)); box-shadow: 0 0 10px rgba(255,186,83,0.6); }
@keyframes app-code-timer { from { transform: scaleX(1); } to { transform: scaleX(0); } }
@media (prefers-reduced-motion: reduce) { .app-station-timer { display: none; } }
/* The result pages (a scan, a gift link, its accept page): one centred column between two
   amber corner brackets; a display-font headline, or the coins big and yellow. */
.app-claim { max-width: 480px; margin: 24px auto; padding: 32px 24px; text-align: center; }
.app-claim.app-bracket::before, .app-claim.app-bracket::after { width: 56px; height: 56px; }
.app-claim > .app-micro { font-size: 11px; color: var(--mid); }
.app-claim .app-lead { font-size: 17px; }
.app-claim .app-btn { width: 100%; margin-top: 8px; }
.app-result-title { margin: 10px 0 12px; font: 700 40px/1.1 var(--font-display); }
.app-scan-amount {
    display: flex; justify-content: center; align-items: baseline; gap: 8px; margin: 10px 0 16px;
    font: 700 64px/1 var(--font-mono); color: var(--yellow);
}
.app-scan-amount small { font-size: 20px; color: var(--mid); }
/* "Redan skannad": a blue ring filled as far as the cooldown has come (by thirds,
   codes.js cooldownRing), the time left in the middle. */
.app-cooldown-ring {
    --passed: 0%; position: relative; display: grid; place-content: center; justify-items: center; gap: 4px;
    width: 148px; height: 148px; margin: 16px auto 8px; border-radius: 50%;
    background: conic-gradient(var(--blue) var(--passed), rgba(255,255,255,0.08) 0);
}
.app-cooldown-ring::before { content: ''; position: absolute; inset: 8px; border-radius: 50%; background: var(--bg); }
.app-cooldown-ring > * { position: relative; }
.app-cooldown-ring-33 { --passed: 33%; }
.app-cooldown-ring-66 { --passed: 66%; }
.app-cooldown-left { font: 700 20px/1 var(--font-mono); color: var(--text); }
.app-cooldown-left span { font-size: 44px; }
.app-cooldown-label { font: 700 10px/1 var(--font-mono); letter-spacing: 0.18em; text-transform: uppercase; color: var(--mid); }
.app-station-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 2px 0; }
.app-break { word-break: break-all; }

/* Profil: the picture beside the name, with a + while there is none. */
.app-me-title { display: flex; align-items: center; gap: 16px; margin: 0 0 8px; }
.app-me-title h1 { margin: 0; }
.app-me-title .app-lead { margin: 2px 0 4px; }
.app-avatar-edit { position: relative; flex: none; text-decoration: none; }
.app-avatar-plus {
    position: absolute; right: -2px; bottom: -2px; display: grid; place-items: center; width: 24px; height: 24px;
    border-radius: 50%; background: var(--yellow); color: var(--bg); font: 700 16px/1 var(--font-mono); box-shadow: 0 0 0 3px var(--bg);
}
/* Välj profilbild: the presets as radios in a grid; the chosen one ringed. */
.app-avatar-form { display: grid; gap: 16px; }
.app-avatar-preview { display: flex; align-items: center; gap: 14px; padding: 16px; background: var(--surface); border-radius: 14px; }
.app-avatar-preview > span:last-child { display: flex; flex-direction: column; gap: 4px; }
.app-avatar-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; margin: 0; padding: 16px; border: 0; background: var(--surface); border-radius: 14px; }
.app-avatar-option { position: relative; display: grid; place-items: center; cursor: pointer; }
.app-avatar-option input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.app-avatar-option img { width: 64px; height: 64px; max-width: 100%; height: auto; border-radius: 50%; }
.app-avatar-option input:checked + img { box-shadow: 0 0 0 3px var(--surface), 0 0 0 5px var(--xp), 0 0 18px rgba(76,182,236,0.6); }
.app-avatar-option input:focus-visible + img { outline: 2px solid var(--blue); outline-offset: 6px; }
.app-avatar-cancel { justify-self: center; }

/* ───────── Admin: an order (35) ───────── */
.app-admin-title { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.app-admin-title h1 { margin: 0; }
.app-pill { padding: 5px 10px; border-radius: 999px; font: 700 10.5px/1 var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase; }
.app-pill-ok { color: var(--green); background: var(--green-tint); border: 1px solid var(--green-line); }
.app-pill-off { color: var(--orange); background: var(--orange-tint); border: 1px solid var(--orange); }
.app-fact-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 10px; margin: 16px 0 24px; }
.app-fact-cards > div { padding: 12px 14px; background: var(--surface); border-radius: 10px; min-width: 0; }
.app-fact-cards dt { font: 700 10px/1 var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--dim); margin-bottom: 6px; }
.app-fact-cards dd { margin: 0; overflow-wrap: anywhere; }
.app-mono { font-family: var(--font-mono); }
.app-row-off td { color: var(--dim); }
.app-ticket-table td { vertical-align: top; }
/* One small form per action; the destructive one is a red link that asks again (<details>). */
.app-admin-inline { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; }
.app-admin-inline .app-row-form input.app-input-seat { width: 72px; }
.app-admin-inline .app-row-form input.app-input-email { width: 100%; max-width: 160px; }
.app-confirm { display: inline-block; }
.app-confirm > summary { list-style: none; }
.app-confirm > summary::-webkit-details-marker { display: none; }
.app-link-danger { color: var(--orange); text-decoration: underline; text-underline-offset: 3px; white-space: nowrap; cursor: pointer; font-size: 14px; }
.app-confirm[open] > summary { color: var(--mid); }
.app-confirm form { margin-top: 6px; }
/* Audit data as chips; the raw JSON is the title. */
.app-kv { display: inline-flex; flex-wrap: wrap; gap: 4px; }
.app-kv-chip { padding: 3px 7px; border-radius: 6px; background: var(--surface-2); border: 1px solid var(--border-soft); font: 12px/1.3 var(--font-mono); color: var(--mid); white-space: nowrap; }
.app-kv-chip b { color: var(--text); font-weight: 700; }

/* ───────── Admin: the seats (37) ───────── */
.app-stat-chips { list-style: none; display: flex; flex-wrap: wrap; gap: 8px; margin: 0; padding: 0; }
.app-stat-chips li {
    padding: 7px 12px; border-radius: 999px; background: var(--surface); border: 1px solid var(--border);
    font: 700 10.5px/1 var(--font-mono); letter-spacing: 0.12em; text-transform: uppercase; color: var(--mid);
}
.app-stat-chips b { color: var(--text); margin-left: 4px; }
.app-stat-chips a { color: inherit; text-decoration: none; }
.app-inline-details { margin-top: 6px; }
.app-inline-details summary { cursor: pointer; color: var(--mid); font-size: 13px; }
.app-stat-chips .app-stat-chip-warn, .app-stat-chips .app-stat-chip-warn b { color: var(--orange); border-color: var(--orange); }
.app-admin-map .seatmap-scroll { max-height: 560px; }
.app-admin-map .seatmap-scroll:not(.seatmap-zoomed) .seatmap-svg { max-height: 560px; }
/* "Vem sitter var": a card per section; seat, nickname (links to the order), order no. */
.app-who-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; margin-bottom: 24px; }
.app-who-card { padding: 14px 16px; background: var(--surface); border-radius: 12px; min-width: 0; }
.app-who-card h3 { margin: 0 0 10px; font: 700 10.5px/1 var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--yellow); }
.app-who-card ul { list-style: none; margin: 0; padding: 0; }
.app-who-card li { display: grid; grid-template-columns: 34px 1fr auto; gap: 8px; align-items: baseline; padding: 4px 0; font-size: 14px; }
.app-who-card li a { color: var(--yellow); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.app-who-card li .app-mono { font-size: 12.5px; }

/* The visitor's own code (my-code.handlebars) and a station's scanner (scanner.handlebars, scanner.js). */
.app-code-who { justify-content: center; margin: 8px 0 16px; }
.app-scanner-video { display: block; width: min(92vw, 460px); aspect-ratio: 1; object-fit: cover; margin: 0 auto; border-radius: 16px; background: #000; }
.app-scanner-result { min-height: 150px; margin: 16px auto 0; padding: 16px; max-width: 460px; border-radius: 16px; background: rgba(255,255,255,0.06); display: flex; flex-direction: column; align-items: center; gap: 6px; }
.app-scanner-result.is-ok { background: rgba(255, 186, 83, 0.14); box-shadow: inset 0 0 0 1px var(--yellow); }
.app-scanner-result.is-no { background: rgba(255, 104, 66, 0.12); box-shadow: inset 0 0 0 1px var(--orange); }
.app-scanner-big { font: 700 32px/1.1 var(--font-mono); margin: 0; }
.app-scanner-result.is-ok .app-scanner-big { color: var(--yellow); }

/* ───────── Rewards: the moment of getting something (rewards.js) ───────── */
/* The Beacon coin (coin.handlebars): a yellow neon outline, a rim and an edge round
   the Beacon logo (the lighthouse and its signal arcs), glowing. Small by default; .app-coin-md
   on the station screen, .app-coin-lg after a scan. */
.app-coin {
    --s: 22px; display: inline-block; flex: none; width: var(--s); height: var(--s); overflow: visible;
    color: var(--yellow); vertical-align: middle;
    filter: drop-shadow(0 0 3px rgba(255,186,83,0.8)) drop-shadow(0 0 8px rgba(255,186,83,0.35));
}
.app-coin circle, .app-coin path { fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; }
.app-coin .app-coin-rim { stroke-width: 2.6; }
.app-coin .app-coin-edge { stroke-width: 1.2; opacity: 0.55; }
/* The logo, from assets/logomark_color_light.svg, filled in the coin's one colour. */
.app-coin .app-coin-logo path { fill: currentColor; stroke: none; }
.app-coin .app-coin-spark { stroke: #fff6dc; stroke-width: 3.4; stroke-dasharray: 0.12 0.88; opacity: 0; }
.app-coin-lg { --s: 112px; }

/* After a scan the coin lights up like a neon sign: the rim draws itself, then the
   edge, the lighthouse flickers on, its signal arcs light outwards, a spark runs round the
   rim and a ring pulses out; small coins burst from it. Then it hangs there, floating
   a few pixels up and down with its glow breathing. */
.app-coin-burst { position: relative; display: grid; place-items: center; height: 140px; margin: 12px 0 4px; }
.app-coin-burst::before {
    content: ''; position: absolute; left: 50%; top: 50%; width: 112px; height: 112px; margin: -56px 0 0 -56px;
    border-radius: 50%; border: 2px solid var(--yellow); opacity: 0; pointer-events: none;
    animation: app-coin-ring 0.8s ease-out 1.15s both;
}
.app-coin-burst > .app-coin-lg .app-coin-rim { stroke-dasharray: 1; animation: app-draw 0.7s cubic-bezier(.4,0,.2,1) 0.1s both; }
.app-coin-burst > .app-coin-lg .app-coin-edge { stroke-dasharray: 1; animation: app-draw 0.6s ease-out 0.35s both; }
.app-coin-burst > .app-coin-lg .app-coin-house { animation: app-coin-on 0.6s steps(1, end) 0.65s both; }
/* The signal arcs light from the lighthouse outwards, as when its light comes on. */
.app-coin-burst > .app-coin-lg .app-coin-wave { animation: app-coin-on 0.45s steps(1, end) 1s both; }
.app-coin-burst > .app-coin-lg .app-coin-wave-2 { animation-delay: 1.12s; }
.app-coin-burst > .app-coin-lg .app-coin-wave-3 { animation-delay: 1.24s; }
.app-coin-burst > .app-coin-lg .app-coin-spark { animation: app-coin-spark 0.9s ease-in-out 1.15s both; }
.app-coin-burst > .app-coin-lg { animation: app-coin-float 4s ease-in-out 2.1s infinite, app-coin-breathe 4s ease-in-out 2.1s infinite; }
@keyframes app-draw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@keyframes app-coin-on { 0% { opacity: 0; } 20% { opacity: 1; } 35% { opacity: 0.15; } 55% { opacity: 1; } 70% { opacity: 0.3; } 85%, 100% { opacity: 1; } }
@keyframes app-coin-spark { 0% { opacity: 0; stroke-dashoffset: 0; } 10%, 85% { opacity: 1; } 100% { opacity: 0; stroke-dashoffset: -1; } }
@keyframes app-coin-ring { 0% { opacity: 0.9; transform: scale(0.8); } 100% { opacity: 0; transform: scale(1.7); } }
@keyframes app-coin-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }
@keyframes app-coin-breathe {
    0%, 100% { filter: drop-shadow(0 0 3px rgba(255,186,83,0.8)) drop-shadow(0 0 8px rgba(255,186,83,0.35)); }
    50% { filter: drop-shadow(0 0 5px rgba(255,186,83,1)) drop-shadow(0 0 18px rgba(255,186,83,0.55)); }
}
.app-coin-fly { position: absolute; left: 50%; top: 50%; margin: -11px 0 0 -11px; opacity: 0; animation: app-coin-fly 0.95s cubic-bezier(.15,.7,.3,1) 1.15s both; }
.app-coin-fly-1 { --x: -118px; --y: -46px; --r: -160deg; }
.app-coin-fly-2 { --x: -84px;  --y: -96px; --r: 120deg; animation-delay: 1.19s; }
.app-coin-fly-3 { --x: -20px;  --y: -120px; --r: -90deg; animation-delay: 1.15s; }
.app-coin-fly-4 { --x: 46px;   --y: -110px; --r: 200deg; animation-delay: 1.23s; }
.app-coin-fly-5 { --x: 104px;  --y: -70px; --r: -140deg; }
.app-coin-fly-6 { --x: 126px;  --y: 4px;   --r: 90deg; animation-delay: 1.21s; }
.app-coin-fly-7 { --x: -128px; --y: 18px;  --r: 160deg; animation-delay: 1.25s; }
.app-coin-fly-8 { --x: 70px;   --y: 56px;  --r: -200deg; animation-delay: 1.17s; }
@keyframes app-coin-fly {
    0% { opacity: 0; transform: translate(0, 0) scale(0.3); }
    15% { opacity: 1; }
    70% { opacity: 1; }
    100% { opacity: 0; transform: translate(var(--x), var(--y)) rotate(var(--r)) scale(1); }
}
.app-claim .app-scan-amount { animation: app-pop 0.5s cubic-bezier(.2,1.6,.4,1) 1.15s both; }
@keyframes app-pop { from { transform: scale(0.6); opacity: 0; } to { transform: scale(1); opacity: 1; } }

/* Achievement unlocked: the card springs in, its icon pops and a shine sweeps across. */
.app-unlocked { --d: 0s; position: relative; overflow: hidden; animation: app-unlock-in 0.6s cubic-bezier(.2,1.4,.4,1) var(--d) both; }
.app-unlocked::after {
    content: ''; position: absolute; inset: 0; pointer-events: none;
    background: linear-gradient(105deg, transparent 35%, rgba(190,232,255,0.35) 50%, transparent 65%);
    transform: translateX(-120%); animation: app-shine 1.1s ease-in-out calc(var(--d) + 0.45s) both;
}
.app-unlocked .app-badge-icon { animation: app-icon-pop 0.6s cubic-bezier(.2,1.8,.4,1) calc(var(--d) + 0.3s) both; }
/* Its icon lights like the coin: the lines draw themselves, then the glow flares and settles.
   The dash properties reach the sprite's lines through <use> (tools/build-icons.mjs gives each
   pathLength="1"). */
.app-unlocked .app-ach-icon {
    stroke-dasharray: 1;
    animation: app-draw 0.9s cubic-bezier(.4,0,.2,1) calc(var(--d) + 0.35s) both, app-icon-flare 0.8s ease-out calc(var(--d) + 1.15s) both;
}
@keyframes app-icon-flare {
    0% { filter: drop-shadow(0 0 4px rgba(76,182,236,0.8)); }
    35% { filter: drop-shadow(0 0 6px rgba(190,232,255,1)) drop-shadow(0 0 16px rgba(76,182,236,0.9)); }
    100% { filter: drop-shadow(0 0 4px rgba(76,182,236,0.8)); }
}
.app-claim .app-unlocked { --d: 1.5s; }
.app-claim .app-unlocked + .app-unlocked { --d: 1.85s; }
@keyframes app-unlock-in {
    from { opacity: 0; transform: translateY(18px) scale(0.85); box-shadow: 0 0 0 rgba(76,182,236,0); }
    50% { box-shadow: 0 0 22px rgba(76,182,236,0.7), 0 0 50px rgba(76,182,236,0.25); }
    to { opacity: 1; transform: none; box-shadow: 0 0 0 rgba(76,182,236,0); }
}
@keyframes app-shine { to { transform: translateX(120%); } }
@keyframes app-icon-pop { 0% { transform: scale(0) rotate(-30deg); } 100% { transform: none; } }

/* Ny nivå: over the whole page. The number in blue neon (levels are blue), the
   lighthouse's waves pulsing out from it and a laser drawn under it, as in the
   logo on the home page. */
.app-levelup {
    --later: 0s; position: fixed; inset: 0; z-index: 100; display: grid; place-items: center; padding: 16px;
    background: rgba(5,8,15,0.86); backdrop-filter: blur(6px);
    animation: app-fade-in 0.3s var(--later) both, app-fade-out 0.5s calc(var(--later) + 7s) forwards;
}
.app-levelup.is-held { animation: app-fade-in 0.3s var(--later) both; }
/* After a scan it waits for the coins and the achievement cards to play (rewards.js). */
.app-levelup.is-after { --later: 2.9s; }
@keyframes app-fade-in { from { opacity: 0; } }
@keyframes app-fade-out { to { opacity: 0; visibility: hidden; } }
.app-levelup-card {
    position: relative; isolation: isolate; width: min(100%, 360px); padding: 30px 28px 26px; text-align: center;
    background: radial-gradient(circle at 50% 30%, rgba(36,155,215,0.18), transparent 60%), var(--hero);
    border: 1px solid rgba(76,182,236,0.45); border-radius: 20px;
    box-shadow: 0 0 18px rgba(76,182,236,0.45), 0 0 60px rgba(36,155,215,0.2);
    animation: app-levelup-card 0.7s cubic-bezier(.2,1.5,.4,1) calc(var(--later) + 0.1s) both;
}
.app-levelup-card .app-btn { width: 100%; margin-top: 18px; }
.app-levelup-waves { position: absolute; z-index: -1; left: 50%; top: 18px; width: 320px; margin-left: -160px; height: auto; pointer-events: none; overflow: visible; }
/* The logo's colours, the same on both sides: yellow inside, orange in the middle, blue at the edge. */
.app-levelup-waves path { opacity: 0; stroke: var(--yellow); filter: drop-shadow(0 0 6px rgba(255,186,83,0.85));
    animation: app-wave 1.8s ease-out calc(var(--later) + 0.5s) infinite; }
.app-levelup-waves path:nth-child(2) { stroke: var(--orange); filter: drop-shadow(0 0 6px rgba(255,104,66,0.8)); animation-delay: calc(var(--later) + 0.7s); }
.app-levelup-waves path:nth-child(3) { stroke: var(--blue); filter: drop-shadow(0 0 6px rgba(36,155,215,0.9)); animation-delay: calc(var(--later) + 0.9s); }
@keyframes app-wave { 0% { opacity: 0; } 20% { opacity: 1; } 70% { opacity: 0.25; } 100% { opacity: 0; } }
.app-levelup-number {
    margin: 2px 0 0; font: 800 120px/1 var(--font-display); color: var(--xp-light);
    text-shadow: 0 0 10px rgba(143,214,255,0.9), 0 0 28px rgba(36,155,215,0.8), 0 0 70px rgba(36,155,215,0.45);
    animation: app-levelup-number 0.8s cubic-bezier(.2,1.3,.4,1) calc(var(--later) + 0.3s) both;
}
/* The laser: the logo's orange–yellow–blue line, drawn out from the middle. */
.app-levelup-laser {
    display: block; height: 2px; margin: 6px auto 10px; width: 78%; border-radius: 2px;
    background: var(--laser); box-shadow: 0 0 8px rgba(255,186,83,0.6);
    transform: scaleX(0); animation: app-laser 0.6s cubic-bezier(.2,.8,.2,1) calc(var(--later) + 0.75s) both;
}
@keyframes app-laser { to { transform: scaleX(1); } }
.app-levelup-title { margin: 0 0 8px; font: 700 28px/1.1 var(--font-display); letter-spacing: 0.06em; text-transform: uppercase; }
@keyframes app-levelup-card { from { opacity: 0; transform: scale(0.6); } to { opacity: 1; transform: none; } }
@keyframes app-levelup-number { from { opacity: 0; transform: scale(2.4); filter: blur(8px); } to { opacity: 1; transform: none; filter: none; } }

@media (prefers-reduced-motion: reduce) {
    .app-coin-fly, .app-coin-burst::before, .app-coin .app-coin-spark, .app-unlocked::after { display: none; }
    .app-coin, .app-coin *, .app-unlocked .app-ach-icon, .h5-achievement svg { animation: none !important; }
    .app-coin-burst > .app-coin-lg, .app-claim .app-scan-amount, .app-unlocked, .app-unlocked .app-badge-icon,
    .app-levelup-card, .app-levelup-number, .app-levelup-laser { animation: none; transform: none; }
    .app-levelup-waves path { animation: none; opacity: 0.8; }
}

/* The XP bar (xp-bar.handlebars): fills with the XP just gained, bursts at the
   end of a level, and fills on in the next (rewards.js). */
.app-xpbar { margin: 4px 0 16px; padding: 14px 14px 10px; text-align: left; background: var(--surface); border: 1px solid var(--border-soft); border-radius: 12px; }
.app-xpbar-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 10px; }
.app-xpbar-level { display: inline-flex; align-items: baseline; gap: 6px; }
.app-xpbar-level b { display: inline-block; min-width: 1.2ch; font: 800 28px/1 var(--font-display); color: var(--xp-light); }
.app-xpbar-title { flex: 1; font: 700 15px/1.2 var(--font-display); letter-spacing: 0.04em; text-transform: uppercase; }
.app-xpbar-gain { font: 700 14px/1 var(--font-mono); color: var(--xp); }
.app-xpbar-track { position: relative; }
.app-xpbar-fill {
    display: block; width: 100%; height: 14px; appearance: none; border: 0; border-radius: 7px; overflow: hidden;
    background: var(--surface-2); box-shadow: inset 0 0 0 1px var(--border-soft);
}
.app-xpbar-fill::-webkit-progress-bar { background: var(--surface-2); }
.app-xpbar-fill::-webkit-progress-value { background: linear-gradient(90deg, #1f7fb4, var(--blue) 60%, #7fd0ff); border-radius: 7px; }
.app-xpbar-fill::-moz-progress-bar { background: linear-gradient(90deg, #1f7fb4, var(--blue) 60%, #7fd0ff); border-radius: 7px; }
.app-xpbar-left { margin-top: 8px; color: var(--mid); }

/* While it plays the card springs in and the gain chip pops. */
.app-xpbar.is-playing { animation: app-unlock-in 0.45s cubic-bezier(.2,1.4,.4,1) both; }
.app-xpbar.is-playing .app-xpbar-gain { animation: app-pop 0.45s cubic-bezier(.2,1.6,.4,1) 0.15s both; }
/* Full: the bar flashes gold, sparks fly from its end and the new level number pops. */
.app-xpbar.is-burst .app-xpbar-fill { animation: app-xp-flash 0.6s ease-out; }
.app-xpbar.is-burst .app-xpbar-level b { animation: app-xp-level 0.6s cubic-bezier(.2,1.8,.4,1); }
.app-xpbar.is-burst .app-xpbar-title { animation: app-pop 0.45s cubic-bezier(.2,1.6,.4,1) 0.1s both; }
@keyframes app-xp-flash {
    0% { box-shadow: 0 0 0 0 rgba(143,214,255,0.9), 0 0 30px rgba(76,182,236,0.9); filter: brightness(2) saturate(0.4); }
    100% { box-shadow: 0 0 0 10px rgba(143,214,255,0), 0 0 0 rgba(76,182,236,0); filter: none; }
}
@keyframes app-xp-level { 0% { transform: scale(2.2); text-shadow: 0 0 24px var(--xp-light); } 100% { transform: none; } }
.app-xpbar-sparks { position: absolute; right: 0; top: 50%; width: 0; height: 0; pointer-events: none; }
.app-xpbar-sparks > i {
    position: absolute; left: -4px; top: -4px; width: 8px; height: 8px; border-radius: 50%;
    background: var(--yellow); box-shadow: 0 0 8px var(--yellow); opacity: 0;
}
.app-xpbar.is-burst .app-xpbar-sparks > i { animation: app-spark 0.7s cubic-bezier(.15,.7,.3,1) both; }
.app-xpbar-sparks > i:nth-child(1)  { --x: -60px; --y: -38px; }
.app-xpbar-sparks > i:nth-child(2)  { --x: -24px; --y: -52px; }
.app-xpbar-sparks > i:nth-child(3)  { --x: 10px;  --y: -48px; }
.app-xpbar-sparks > i:nth-child(4)  { --x: 34px;  --y: -22px; }
.app-xpbar-sparks > i:nth-child(5)  { --x: 40px;  --y: 8px; }
.app-xpbar-sparks > i:nth-child(6)  { --x: 26px;  --y: 34px; }
.app-xpbar-sparks > i:nth-child(7)  { --x: -8px;  --y: 44px; }
.app-xpbar-sparks > i:nth-child(8)  { --x: -46px; --y: 30px; }
.app-xpbar-sparks > i:nth-child(9)  { --x: -84px; --y: -8px; background: var(--blue); box-shadow: 0 0 8px var(--blue); }
.app-xpbar-sparks > i:nth-child(10) { --x: -70px; --y: 20px; background: var(--blue); box-shadow: 0 0 8px var(--blue); }
@keyframes app-spark {
    0% { opacity: 1; transform: translate(0, 0) scale(1.4); }
    100% { opacity: 0; transform: translate(var(--x), var(--y)) scale(0.4); }
}
/* Ny nivå waits for the bar to fill into the new level (rewards.js). */
.app-levelup.is-waiting { display: none; }
@media (prefers-reduced-motion: reduce) {
    .app-xpbar-sparks { display: none; }
    .app-xpbar.is-playing, .app-xpbar.is-playing .app-xpbar-gain, .app-xpbar.is-burst .app-xpbar-fill,
    .app-xpbar.is-burst .app-xpbar-level b, .app-xpbar.is-burst .app-xpbar-title { animation: none; }
}

/* ───────── Neon from the home page, used sparingly ───────── */
/* The signal: the home page's hero ticket card has a glowing line along its top
   edge (home5.css .h5-hero-urgency::before). Here it marks the one main card of a
   page (Hem's hero, Profil's player card): <span class="app-signal"> as the card's
   first child, yellow, or blue (.is-xp) on what is about levels. */
.app-signal {
    --signal-rgb: 255,186,83;
    /* On the card's own top border, so it is the edge that lights up. */
    position: absolute; top: -1px; left: 8%; right: 8%; height: 1px; z-index: 1; pointer-events: none;
    background: rgb(var(--signal-rgb));
    -webkit-mask-image: linear-gradient(90deg, transparent, #000, transparent);
            mask-image: linear-gradient(90deg, transparent, #000, transparent);
    box-shadow: 0 0 12px rgba(var(--signal-rgb), 0.6);
}
.app-signal.is-xp { --signal-rgb: 76,182,236; }

/* The beam: the home page's card hover (home5.css CARD HOVER). On a card you can
   click, the pointer lifts it and a neon ring in its colour spins round its edge.
   A child <span class="app-beam">, since cards use ::before/::after already. Only
   on links and buttons: a card that does nothing shouldn't promise something. */
@property --app-beam-angle { syntax: '<angle>'; inherits: false; initial-value: 0deg; }
.app-beam-card {
    --card-rgb: 255,186,83;
    position: relative;
    transition: border-color 0.25s ease, transform 0.25s ease, box-shadow 0.25s ease;
}
.app-beam-card.is-xp { --card-rgb: 76,182,236; }
.app-beam-card:hover, .app-beam-card:focus-visible {
    transform: translateY(-3px); border-color: transparent;
    box-shadow: 0 0 0 1px rgba(var(--card-rgb), 0.5), 0 18px 40px -16px rgba(var(--card-rgb), 0.55);
}
.app-beam {
    position: absolute; inset: 0; z-index: 2; border-radius: inherit; padding: 2px; pointer-events: none; opacity: 0;
    background: conic-gradient(from var(--app-beam-angle),
        transparent 0deg, rgba(var(--card-rgb), 1) 50deg, transparent 95deg,
        transparent 180deg, rgba(var(--card-rgb), 1) 230deg, transparent 275deg);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
            mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
    transition: opacity 0.3s ease;
    animation: app-beam-spin 2.4s linear infinite;
}
.app-beam-card:hover > .app-beam, .app-beam-card:focus-visible > .app-beam { opacity: 1; }
@keyframes app-beam-spin { to { --app-beam-angle: 360deg; } }
@media (prefers-reduced-motion: reduce) {
    .app-beam { animation: none; }
    .app-beam-card:hover, .app-beam-card:focus-visible { transform: none; }
}
.app-mission.app-beam-card:hover { border-color: transparent; }

/* ───────── Profil (me.handlebars) ───────── */
/* The player card: who you are in the game, at a glance. */
.app-player { position: relative; margin: 0 0 16px; padding: 20px 18px 18px; background: var(--hero); border: 1px solid var(--border-soft); border-radius: 16px; }
.app-player-who { display: flex; align-items: center; gap: 16px; }
.app-player-who h1 { margin: 0; }
.app-player-games .app-player-who .app-avatar { box-shadow: 0 0 14px rgba(76,182,236,0.45); }
.app-player-level { margin: 2px 0 4px; font: 700 13px var(--font-mono); letter-spacing: 0.12em; text-transform: uppercase; color: var(--xp); }
.app-player .app-progress { margin-top: 18px; height: 10px; border-radius: 5px; }
.app-player-coins { display: flex; align-items: center; gap: 10px; margin: 16px 0 0; padding-top: 14px; border-top: 1px solid var(--border-soft); }
.app-player-coins b { font: 700 28px/1 var(--font-mono); color: var(--yellow); }
.app-player-coins small { color: var(--mid); font-size: 13px; }

/* Skanna and Visa min kod, side by side. */
.app-quick { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 0 0 8px; }
.app-quick-card {
    display: flex; flex-direction: column; gap: 4px; padding: 14px; text-decoration: none; color: var(--text);
    background: var(--surface); border: 1px solid var(--border-soft); border-radius: 14px;
}
.app-quick-card .app-icon { width: 26px; height: 26px; color: rgb(var(--card-rgb)); margin-bottom: 6px; }
.app-quick-card strong { font: 700 16px/1.2 var(--font-display); }
.app-quick-card .app-small { margin: 0; color: var(--mid); font-size: 13px; line-height: 1.35; }

/* Nära att låsas upp: what's closest, to give the next thing to go for. */
.app-near { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.app-near li { display: flex; align-items: center; gap: 12px; padding: 12px; background: var(--surface); border: 1px solid var(--border-soft); border-radius: 12px; }
.app-near li > div { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.app-near .app-progress { margin-top: 8px; height: 6px; }
.app-near-count { font: 700 12px var(--font-mono); color: var(--mid); margin-top: 4px; }

/* The collection: every achievement as a tile, taken ones lit; a tap opens its details. */
.app-trophies { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(64px, 1fr)); gap: 10px; }
.app-trophy {
    --card-rgb: 120,140,170;
    position: relative; display: grid; place-items: center; width: 100%; aspect-ratio: 1; padding: 0; cursor: pointer;
    font: inherit; color: var(--dim); background: var(--surface); border: 1px solid var(--border-soft); border-radius: 14px;
}
.app-trophy.is-earned { --card-rgb: 76,182,236; color: var(--text); border-color: rgba(76,182,236,0.35); background: radial-gradient(circle at 50% 35%, rgba(76,182,236,0.16), transparent 70%), var(--surface); }
/* The icon is a line drawing (ach-icon.handlebars): dim grey until taken, then a blue neon tube. */
.app-trophy-icon { display: grid; color: #4a5872; }
.app-trophy-icon .app-ach-icon { width: 30px; height: 30px; }
.app-trophy.is-earned .app-trophy-icon { color: var(--xp-light); filter: drop-shadow(0 0 4px rgba(76,182,236,0.85)) drop-shadow(0 0 10px rgba(36,155,215,0.45)); }
.app-trophy-times { position: absolute; top: 4px; right: 6px; font: 700 10px var(--font-mono); color: var(--xp); }
.app-trophy-progress { position: absolute; left: 8px; right: 8px; bottom: 7px; width: auto; height: 3px; }
.app-trophy-pop {
    width: min(320px, calc(100vw - 32px)); padding: 18px; color: var(--text); text-align: left;
    background: var(--surface); border: 1px solid rgba(76,182,236,0.45); border-radius: 16px;
    box-shadow: 0 20px 60px rgba(0,0,0,0.6), 0 0 24px rgba(76,182,236,0.2);
}
.app-trophy-pop::backdrop { background: rgba(5,8,15,0.6); }
.app-trophy-pop-head { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
.app-trophy-pop-head strong { font: 700 18px/1.2 var(--font-display); }
.app-trophy-pop .app-progress { margin-top: 10px; height: 6px; }
.app-trophy-pop .app-badge-reward { display: block; margin-top: 12px; text-align: left; }
.app-trophy-pop .app-btn { width: 100%; margin-top: 14px; min-height: 44px; }

/* Historik: the newest, the rest a tap away. */
.app-more summary { cursor: pointer; padding: 10px 0; color: var(--yellow); font: 700 12px var(--font-mono); letter-spacing: 0.15em; text-transform: uppercase; }
.app-account-email { margin: 0 0 4px; color: var(--mid); }

/* ───────── Achievement icons, Prestationer and Historik ───────── */
/* Tabler line icons (public/app/ach-icons.svg) in place of emoji: the same on every
   phone, and lit like the home page's neon when taken. */
.app-ach-icon { display: block; width: 22px; height: 22px; }
.app-badge-icon { color: #6b7d99; }
.app-badge-icon.is-earned, .app-near .app-badge-icon { color: var(--xp-light); }
.app-badge-icon.is-earned .app-ach-icon { filter: drop-shadow(0 0 4px rgba(76,182,236,0.8)); }

/* Profil's "Senast tagna": one row of up to five tiles. */
.app-trophies-row { grid-template-columns: repeat(5, minmax(0, 1fr)); margin: 6px 0 12px; }
.app-link-card {
    display: flex; justify-content: space-between; align-items: center; padding: 14px 16px; text-decoration: none;
    color: var(--text); font: 700 15px var(--font-display); background: var(--surface);
    border: 1px solid var(--border-soft); border-radius: 12px;
}
.app-link-card > span:last-child { color: var(--xp); font-size: 20px; }
.app-block-head .app-eyebrow { margin: 0; }
.app-block-head a { font: 700 12px var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase; text-decoration: none; }

/* The pages under Profil: a way back. */
.app-back { margin: 0 0 14px; }
.app-back a { color: var(--mid); text-decoration: none; font: 700 12px var(--font-mono); letter-spacing: 0.12em; text-transform: uppercase; }
.app-back a:hover { color: var(--yellow); }
.app-chip small { opacity: 0.7; margin-left: 4px; }

/* Historik, by day. */
.app-history-day { margin-top: 18px; }
.app-history-day .app-micro { color: var(--mid); text-transform: uppercase; }
.app-stat-chips .c-coins { color: var(--yellow); }
.app-stat-chips .c-xp { color: var(--xp); }

/* ───────── The station screen: the home page's neon, for the hall ───────── */
/* Big and calm: it reloads every 30 s for a new code, so it has steady loops only
   (the waves, the timer), never an entrance. Side by side on a wide screen, the
   name and how-to left and the code right; stacked on a tablet held upright. */
.app-shell:has(.app-station-screen) { background:
    radial-gradient(60% 50% at 15% 10%, rgba(36,155,215,0.14), transparent 70%),
    radial-gradient(55% 45% at 90% 95%, rgba(255,104,66,0.12), transparent 70%); }
.app-station-screen { max-width: 1100px; display: grid; gap: 28px; align-items: center; padding: 8px 0 24px; }
@media (min-width: 900px) and (min-aspect-ratio: 4/3) {
    .app-station-screen { grid-template-columns: 1fr auto; text-align: left; gap: 56px; }
    .app-station-screen .app-station-laser { margin-left: 0; }
    .app-station-screen .app-station-info .app-station-coins { justify-content: flex-start; }
    .app-station-screen .app-station-how { margin-left: 0; }
}
.app-station-screen h1 { margin: 10px 0 0; font-size: clamp(40px, 6vw, 84px); line-height: 1; text-shadow: 0 0 30px rgba(36,155,215,0.25); }
.app-station-laser {
    display: block; height: 3px; width: min(100%, 420px); margin: 18px auto; border-radius: 3px;
    background: var(--laser); box-shadow: 0 0 12px rgba(255,186,83,0.55);
}
.app-station-screen .app-station-coins { display: flex; align-items: center; justify-content: center; gap: 14px; margin: 0 0 22px; font-size: clamp(40px, 5vw, 64px); }
.app-station-screen .app-station-coins small { font-size: 0.4em; color: var(--mid); }
.app-coin-md { --s: 58px; }
.app-station-how { margin: 0 auto; padding: 0; list-style: none; counter-reset: how; display: grid; gap: 10px; max-width: 460px; color: var(--mid); font-size: clamp(16px, 1.6vw, 20px); }
.app-station-how li { counter-increment: how; display: flex; gap: 12px; align-items: baseline; text-align: left; }
.app-station-how li::before { content: counter(how); flex: none; display: grid; place-items: center; width: 28px; height: 28px; border-radius: 50%; border: 1px solid rgba(76,182,236,0.6); color: var(--xp-light); font: 700 14px/1 var(--font-mono); box-shadow: 0 0 10px rgba(76,182,236,0.35); }
.app-station-how b { color: var(--text); }
.app-station-code { display: flex; flex-direction: column; align-items: center; }
.app-station-stage { display: flex; align-items: center; gap: 6px; }
.app-station-screen .app-station-qr {
    position: relative; width: min(70vw, 58vh, 480px); padding: 14px; border-radius: 18px;
    box-shadow: 0 0 0 2px rgba(255,255,255,0.9), 0 0 24px rgba(36,155,215,0.55), 0 0 70px rgba(255,186,83,0.25);
}
.app-station-screen .app-signal { top: -7px; height: 2px; --signal-rgb: 255,186,83; }
.app-station-waves { width: clamp(40px, 7vw, 90px); height: auto; overflow: visible; fill: none; stroke-width: 6; stroke-linecap: round; }
.app-station-waves.is-left { stroke: var(--orange); filter: drop-shadow(0 0 6px rgba(255,104,66,0.85)); }
.app-station-waves.is-right { stroke: var(--blue); filter: drop-shadow(0 0 6px rgba(36,155,215,0.9)); }
.app-station-waves path { opacity: 0.15; animation: app-station-wave 2.4s ease-out infinite; }
.app-station-waves path:nth-child(2) { animation-delay: 0.25s; }
.app-station-waves path:nth-child(3) { animation-delay: 0.5s; }
@keyframes app-station-wave { 0% { opacity: 0.15; } 25% { opacity: 1; } 70% { opacity: 0.3; } 100% { opacity: 0.15; } }
.app-station-screen .app-station-timer { width: min(70vw, 58vh, 480px); height: 5px; margin-top: 22px; }
@media (max-width: 520px) { .app-station-waves { display: none; } }
@media (prefers-reduced-motion: reduce) { .app-station-waves path { animation: none; opacity: 0.8; } }
.app-main-wide:has(.app-station-screen) { max-width: 1240px; }

/* ───────── The way in: Logga in, the code, a new account ───────── */
/* The bridge from the home page: the lighthouse logo glowing, the logo's laser
   under the heading, the form card's top edge lit (.app-signal) and the home
   page's blue and orange glow behind. */
.app-shell:has(.app-auth) { background:
    radial-gradient(60% 45% at 10% 0%, rgba(36,155,215,0.14), transparent 70%),
    radial-gradient(55% 40% at 95% 100%, rgba(255,104,66,0.1), transparent 70%); }
.app-auth-logo { display: block; width: 64px; height: auto; margin: 4px 0 20px; filter: drop-shadow(0 0 10px rgba(255,186,83,0.35)) drop-shadow(0 0 22px rgba(36,155,215,0.25)); }
.app-auth h1 { margin-bottom: 0; }
.app-auth-laser { display: block; height: 2px; width: min(100%, 220px); margin: 14px 0 16px; border-radius: 2px; background: var(--laser); box-shadow: 0 0 10px rgba(255,186,83,0.5); }
.app-auth .app-card { background: linear-gradient(165deg, rgba(36,155,215,0.06), rgba(255,186,83,0.03) 50%, rgba(255,104,66,0.05)), var(--surface); }
/* The station screen's coin hangs and breathes like the one after a scan (no drawing:
   the screen reloads every 30 s). */
.app-station-coins .app-coin { animation: app-coin-float 4s ease-in-out infinite, app-coin-breathe 4s ease-in-out infinite; }

/* What you got since the last page (reward.handlebars), on top of any page. */
.app-reward { position: relative; margin: 0 0 18px; padding-top: 4px; overflow: hidden; transition: max-height 0.45s ease, opacity 0.35s ease, margin 0.45s ease; }
.app-reward .app-xpbar { margin-bottom: 0; }
.app-reward.is-closing { opacity: 0; margin-bottom: 0; }
/* The ×: on the first card's corner, out of the way of its text. */
.app-reward-close {
    position: absolute; top: 10px; right: 6px; z-index: 3; display: grid; place-items: center; width: 32px; height: 32px;
    padding: 0; font: 400 22px/1 var(--font-body); color: var(--mid); background: transparent; border: 0; border-radius: 50%; cursor: pointer;
}
.app-reward-close:hover, .app-reward-close:focus-visible { color: var(--text); background: rgba(255,255,255,0.08); }
.app-reward .app-unlocked:first-of-type, .app-reward > .app-xpbar:first-of-type { padding-right: 44px; }
@media (prefers-reduced-motion: reduce) { .app-reward { transition: none; } }

/* ───────── Hem's game row (home.handlebars) ───────── */
/* Where you stand in the game, at a glance, opening Profil: the picture in its level ring,
   the level, the XP bar, coins and the closest to unlock. Still: the hero below is the page's
   lit card; this one only has the neon ring under the pointer, as a card you can open. */
.app-gamerow {
    position: relative; display: flex; align-items: center; gap: 14px; margin: 0 0 16px; padding: 14px 14px 12px;
    color: var(--text); text-decoration: none; background: var(--hero); border: 1px solid var(--border-soft); border-radius: 16px;
}
.app-gamerow > .app-avatar { width: 48px; height: 48px; font-size: 20px; box-shadow: 0 0 12px rgba(76,182,236,0.4); }
.app-gamerow-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.app-gamerow-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.app-gamerow-who { min-width: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px; }
.app-gamerow-who b { font: 700 16px/1.2 var(--font-display); }
.app-gamerow-level { font: 700 11px var(--font-mono); letter-spacing: 0.12em; text-transform: uppercase; color: var(--xp); }
.app-gamerow-coins { flex: none; display: inline-flex; align-items: center; gap: 6px; }
.app-gamerow-coins b { font: 700 18px/1 var(--font-mono); color: var(--yellow); }
.app-gamerow .app-progress { height: 6px; }
.app-gamerow-sub { font-size: 12.5px; line-height: 1.35; color: var(--mid); }
.app-gamerow-near { color: var(--xp-light); }
.app-gamerow-go { flex: none; font: 700 22px/1 var(--font-mono); color: var(--xp); }

/* A rank above a visitor on Profil (app/users/staff.js rankOf): crew, crewlead, admin. */
.app-rank { display: inline-block; vertical-align: middle; margin-left: 6px; padding: 3px 8px; border-radius: 6px; font: 700 10px/1.2 var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--orange); border: 1px solid rgba(255,104,66,0.5); }
.app-rank-crewlead { color: var(--yellow); border-color: rgba(255,186,83,0.55); }
.app-rank-admin { color: var(--xp-light); border-color: rgba(76,182,236,0.55); }
/* The crewlead's scanner: what to scan for, above the camera. */
.app-crew-pick { margin: 0 0 14px; }
