/* Copenhagen Tourist Cycling Licence - components layered on top of the parent
   site's design system (PRD §8).

   app.css is copied verbatim from bikesafecph.com and carries the tokens,
   typography, buttons, nav and footer. Nothing in here redefines those: this
   file only adds the surfaces that exist on this subdomain and nowhere else -
   the course, the test, the certificate.

   Mobile-first throughout, per the parent PRD §4.3: the test in particular is
   used one-handed, outdoors, on a phone. */

/* ---------------------------------------------------------------- utilities */

/* [hidden] must beat any author `display`.
   The UA stylesheet's `[hidden] { display: none }` loses to any author rule
   that sets display - so `.consent { display: flex }`, `.field-error {
   display: block }` and `.chapter-row-read { display: grid }` each silently
   defeated their own `hidden` attribute. That meant a permanently visible
   cookie banner, a "pick an answer" error on every untouched question, and a
   course that looked fully read on a first visit. One rule, rather than
   remembering this at every call site. */
[hidden] { display: none !important; }

/* The reading container. Only the home page runs full width; every other page
   uses this, so long-form content keeps a sane measure. */
.wrap.narrow { max-width: 760px; }

.center { text-align: center; }
.center-row { justify-content: center; }
.mono { font-family: var(--mono); }
.sub-head { font-size: clamp(21px, 3vw, 27px); margin: 48px 0 18px; letter-spacing: -.01em; }

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

.prose p { margin-bottom: 20px; color: var(--ink-soft); }
.prose h2 { font-size: clamp(21px, 3vw, 27px); margin: 40px 0 14px; letter-spacing: -.012em; }
.prose ul { color: var(--ink-soft); padding-left: 22px; margin-bottom: 20px; }
.prose li { margin-bottom: 9px; }
.prose a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }

.btn--lg { font-size: 17px; padding: 16px 30px; min-height: 56px; }
.btn--on-dark { background: var(--paper); border-color: var(--paper); color: var(--ink); }
.btn--danger { background: #a3261f; border-color: #a3261f; }
.btn--google { background: var(--white); color: var(--ink); border: 1px solid var(--line); width: 100%; justify-content: center; }
.btn--google:hover { background: var(--paper-2); }

.notice {
  padding: 14px 18px; border-radius: var(--r); background: var(--paper-2);
  border-left: 3px solid var(--muted); color: var(--ink-soft); margin: 20px 0; font-size: 16px;
}
.notice--warn { background: #fdf3e7; border-left-color: #c8791f; }
.notice--ok { background: #eef6ee; border-left-color: #3f7a45; }

/* The disclaimer is load-bearing (PRD §9.1). It is muted, never hidden, and
   never smaller than the body text around it by more than a step. */
.disclaimer-inline {
  margin-top: 26px; font-family: var(--mono); font-size: 12.5px;
  letter-spacing: .04em; color: var(--muted); line-height: 1.6;
}

/* The full product name, not the short form: it is what this subdomain is, and
   spelling it out in the chrome saves repeating it on every page.
   Stacked under the wordmark rather than set beside it - as a pill on the same
   line it ran to ~240px and pushed the nav links onto two rows at 1440px. */
.brand-lockup { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.brand-qualifier {
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--accent); white-space: nowrap;
  line-height: 1.2;
}
/* The brand is a row of two items now, so it needs its own line-height back. */
.brand .brand-text { line-height: 1.1; }

.lang-switch { display: flex; gap: 2px; }
.lang-switch a {
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .08em;
  padding: 7px 9px; border-radius: 8px; color: var(--muted); min-height: 34px;
  display: inline-flex; align-items: center;
}
.lang-switch a[aria-current="true"] { background: var(--tint); color: var(--accent); }

/* -------------------------------------------------------------------- home */

/* Hero: the licence card floating on the photo gallery.
   .hero-fade itself is styled in app.css (copied from the parent site) - only
   the layering and the scrim are new here. */
.hero-card { position: relative; }
.hero-card .hero-fade { width: 100%; }

/* A scrim under the card, so a plum card on a bright photo still reads. Sits
   at z-index 1: below the stepper dots (5) so they stay visible, above the
   photos so it actually darkens them. */
.hero-card .hero-fade::after {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    radial-gradient(ellipse at center, rgba(26, 24, 19, .55) 0%, rgba(26, 24, 19, .18) 62%, rgba(26, 24, 19, .3) 100%),
    linear-gradient(to top, rgba(26, 24, 19, .35), transparent 45%);
}

/* pointer-events none on the wrapper, auto on the card: clicks fall through to
   the stepper dots underneath everywhere except the card itself. */
.hero-card-float {
  position: absolute; inset: 0; z-index: 3;
  display: grid; place-items: center; padding: 24px;
  pointer-events: none;
}
.hero-card-float .licence-card {
  pointer-events: auto;
  max-width: min(340px, 84%);
  box-shadow: 0 26px 64px rgba(26, 24, 19, .42);
}

.steps { list-style: none; padding: 0; margin: 40px 0 0; display: grid; gap: 20px; grid-template-columns: 1fr; counter-reset: step; }
.step { background: var(--white); border: 1px solid var(--line); border-radius: var(--r-lg); padding: 26px; }
.step-num { font-family: var(--mono); font-size: 12px; letter-spacing: .16em; color: var(--accent); }
.step h3 { font-size: 19px; margin: 12px 0 8px; }
.step p { color: var(--ink-soft); font-size: 16px; }

.cards { display: grid; gap: 20px; margin-top: 40px; grid-template-columns: 1fr; }
.card { background: var(--white); border: 1px solid var(--line); border-radius: var(--r-lg); padding: 26px; }
.card h3 { font-size: 19px; margin-bottom: 10px; }
.card p { color: var(--ink-soft); font-size: 16px; }

.panel-deep { background: var(--accent-deep); color: var(--paper); border-radius: 28px; margin: 0 24px 72px; padding: 64px 0; }
.panel-deep h2 { font-family: var(--serif); font-weight: 400; font-size: clamp(32px, 5vw, 52px); }
/* app.css only gives .cta-row a top margin inside .hero, so out here the button
   was sitting straight under the headline with nothing between them. */
.panel-deep .cta-row { margin-top: 40px; }

.faq details {
  border-bottom: 1px solid var(--line); padding: 18px 0;
}
.faq summary { cursor: pointer; font-weight: 600; font-size: 17px; list-style: none; padding-right: 24px; position: relative; min-height: 32px; display: flex; align-items: center; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: '+'; position: absolute; right: 0; color: var(--accent); font-size: 22px; line-height: 1; }
.faq details[open] summary::after { content: '\2212'; }
.faq p { margin-top: 12px; color: var(--ink-soft); font-size: 16px; }

/* ------------------------------------------------------------------ course */

.chapter-grid { display: grid; gap: 16px; margin-top: 40px; grid-template-columns: 1fr; }
.chapter-card { background: var(--white); border: 1px solid var(--line); border-radius: var(--r-lg); padding: 24px; display: block; transition: border-color .2s ease, transform .15s ease; }
.chapter-card:hover { border-color: var(--accent); transform: translateY(-2px); }
.chapter-card h3 { font-size: 18px; margin: 10px 0 6px; }
.chapter-card p { color: var(--ink-soft); font-size: 15.5px; }
.chapter-num { font-family: var(--mono); font-size: 12px; letter-spacing: .14em; color: var(--accent); }

.chapter-list { list-style: none; padding: 0; margin: 40px 0 0; }
.chapter-row {
  display: grid; grid-template-columns: auto 1fr auto auto; gap: 16px; align-items: center;
  padding: 22px 4px; border-bottom: 1px solid var(--line); min-height: 64px;
}
.chapter-row:hover .chapter-row-title { color: var(--accent); }
.chapter-row-body { display: flex; flex-direction: column; gap: 4px; }
.chapter-row-title { font-weight: 600; font-size: 17.5px; }
.chapter-row-lede { color: var(--muted); font-size: 15px; }
.chapter-row-read { font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: #3f7a45; }
.chapter-row .arr { color: var(--muted); }
/* The read state is carried by the green check and the dimmed photo. It used
   to green the number too - but inside `.chapter-row` the only `.chapter-num`
   is the badge on the photo, so that rule out-specified the badge's white and
   left dark green on plum: the number read as half-erased once a chapter was
   marked read. */

.chapter-title { font-size: clamp(30px, 5.4vw, 48px); margin: 14px 0 16px; letter-spacing: -.022em; }
.chapter-image { width: 100%; border-radius: var(--r-lg); margin: 32px 0; aspect-ratio: 16 / 9; object-fit: cover; }
.chapter .prose p { font-size: 18px; line-height: 1.65; }

.tested { background: var(--tint); border-radius: var(--r-lg); padding: 26px; margin: 34px 0; }
.tested ul { margin: 14px 0 0; padding-left: 20px; }
.tested li { margin-bottom: 8px; color: var(--ink-soft); font-size: 16px; }

.more-detail { border: 1px solid var(--line); border-radius: var(--r-lg); padding: 26px; margin: 34px 0; }
.more-detail p { color: var(--ink-soft); margin: 10px 0 18px; font-size: 16px; }

.chapter-nav { display: grid; gap: 14px; margin: 48px 0 24px; grid-template-columns: 1fr; }
.chapter-nav-link { border: 1px solid var(--line); border-radius: var(--r-lg); padding: 18px 22px; display: flex; flex-direction: column; gap: 5px; min-height: 64px; justify-content: center; }
.chapter-nav-link:hover { border-color: var(--accent); }
.chapter-nav-link--next { text-align: right; }
.chapter-back { margin-top: 20px; color: var(--muted); font-size: 15px; }

.course-finish { margin-top: 56px; padding: 34px; background: var(--paper-2); border-radius: var(--r-lg); }
.course-finish h2 { font-size: 24px; margin-bottom: 8px; }
.course-finish p { color: var(--ink-soft); margin-bottom: 20px; }

/* ------------------------------------------------------------------- video */

.video-frame { position: relative; aspect-ratio: 16 / 9; border-radius: var(--r-lg); overflow: hidden; background: var(--paper-3); margin: 32px 0 14px; }
.video-play { width: 100%; height: 100%; padding: 0; border: 0; background: none; cursor: pointer; position: relative; display: block; }
.video-play img { width: 100%; height: 100%; object-fit: cover; }
.video-play-btn {
  position: absolute; inset: 0; margin: auto; width: 76px; height: 76px; border-radius: 999px;
  background: var(--accent); color: #fff; display: grid; place-items: center; font-size: 24px;
  padding-left: 5px; box-shadow: 0 10px 30px rgba(26, 24, 19, .3);
}
.video-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.video-credit a { color: var(--accent); text-decoration: underline; }
.video-gate { margin-top: 30px; }

/* -------------------------------------------------------------------- test */

.rules-box { background: var(--paper-2); border-radius: var(--r-lg); padding: 26px; margin: 32px 0; }
.rules-box ul { margin: 14px 0 0; padding-left: 20px; }
.rules-box li { margin-bottom: 9px; color: var(--ink-soft); font-size: 16px; }
/* The language line under the rules on the pre-flight test screen. The
   switch sits on the same line as the statement, because it only makes
   sense as an answer to it. */
.test-language { margin: -18px 0 26px; }
.test-language a { margin-left: 10px; color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
.test-language a + a { margin-left: 14px; }

.test-screen { padding: 32px 0 80px; }
.test-bar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.test-count, .test-timer { font-family: var(--mono); font-size: 12.5px; letter-spacing: .1em; color: var(--muted); text-transform: uppercase; }
.test-timer.is-low { color: #a3261f; font-weight: 500; }

.test-track { height: 5px; background: var(--paper-3); border-radius: 999px; overflow: hidden; position: relative; }
.test-fill { height: 100%; background: var(--accent); border-radius: 999px; transition: width .3s ease; }
.test-track--lg { height: 12px; margin: 22px 0 26px; overflow: visible; border-radius: 999px; }
.test-track--lg .test-fill { border-radius: 999px; }
.test-passmark { position: absolute; top: -5px; bottom: -5px; width: 2px; background: var(--ink); opacity: .35; }

/* The cyclist on the progress bar (test question screen).

   Same drawing as the scrollbar bike, upright instead of rotated, riding along
   the bar as the questions go by. Two things it must not do: fall off either
   end, and move on its own.

   Not falling off is what the rail is for. The bike is centred on the progress
   point, so at 0% and 100% half of it would hang outside the track; the rail is
   the track inset by half a bike at each end, and the percentage is measured
   along that instead.

   Not moving on its own is why the wheel animation inherited from app.css is
   switched off here and re-run as a single burst: the page reloads per
   question, so each answer plays one short ride from the old position to the
   new one, and then the bar is still again. */
.test-screen .test-track { --bike-w: 26px; margin-top: 30px; overflow: visible; }
.test-bike-rail { position: absolute; inset: 0 calc(var(--bike-w) / 2); }
.test-bike {
  position: absolute; left: var(--done); bottom: calc(100% - 2px);
  width: var(--bike-w); transform: translateX(-50%);
  pointer-events: none; line-height: 0;
}
/* The ink and the wheel geometry come from app.css, which the scrollbar bike
   already uses; only the rotation and the drop shadow are `.sb-cycle`'s, and
   this one wants neither. */
.test-bike-cycle { display: block; width: 100%; height: auto; overflow: visible; }
/* Beats `.sb-whl { animation: sb-roll 2s linear infinite }` in app.css. */
.test-bike .sb-whl { animation: none; }

@media (prefers-reduced-motion: no-preference) {
  .test-bike { animation: test-bike-ride .6s cubic-bezier(.33, .1, .25, 1); }
  .test-bike .sb-whl { animation: sb-roll .6s linear 2; }
}

@keyframes test-bike-ride { from { left: var(--was); } }

@media (min-width: 700px) {
  .test-screen .test-track { --bike-w: 34px; }
}

.test-q { font-size: clamp(23px, 4.2vw, 34px); margin: 26px 0 28px; line-height: 1.2; letter-spacing: -.015em; }

.test-options { display: flex; flex-direction: column; gap: 12px; }
.test-option {
  display: flex; align-items: flex-start; gap: 14px; padding: 18px 20px; cursor: pointer;
  background: var(--white); border: 1.5px solid var(--line); border-radius: var(--r-lg);
  transition: border-color .15s ease, background .15s ease; min-height: 60px;
}
.test-option:hover { border-color: var(--muted); }
.test-option input { position: absolute; opacity: 0; pointer-events: none; }
.test-option-mark {
  flex: none; width: 24px; height: 24px; border-radius: 999px; border: 2px solid var(--line);
  margin-top: 1px; display: grid; place-items: center; transition: border-color .15s ease;
}
.test-option-mark::after { content: ''; width: 11px; height: 11px; border-radius: 999px; background: transparent; transition: background .15s ease; }
.test-option:has(input:checked) { border-color: var(--accent); background: var(--tint); }
.test-option:has(input:checked) .test-option-mark { border-color: var(--accent); }
.test-option:has(input:checked) .test-option-mark::after { background: var(--accent); }
.test-option:has(input:focus-visible) { outline: 2px solid var(--ink); outline-offset: 3px; }
.test-option-text { font-size: 17px; line-height: 1.45; }

.test-nav { margin-top: 30px; }
.test-nav .btn { width: 100%; justify-content: center; }

.field-error { color: #a3261f; font-size: 14.5px; margin-top: 10px; display: block; }

/* ------------------------------------------------------------------ result */

.scorecard { text-align: center; margin: 34px 0 0; }
.score { display: flex; align-items: baseline; justify-content: center; gap: 2px; }
.score-num { font-family: var(--serif); font-size: clamp(64px, 14vw, 110px); line-height: 1; }
.score-max { font-family: var(--serif); font-size: clamp(26px, 5vw, 40px); color: var(--muted); }
.score-label { font-family: var(--mono); font-size: 11.5px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); margin-top: 8px; }
.result--pass .score-num { color: var(--accent); }

.breakdown { list-style: none; padding: 0; margin: 0; }
.breakdown-row a { display: flex; justify-content: space-between; align-items: center; gap: 16px; padding: 16px 4px; border-bottom: 1px solid var(--line); min-height: 56px; }
.breakdown-title { font-size: 16.5px; }
.breakdown-score { font-family: var(--mono); font-size: 14px; color: var(--muted); white-space: nowrap; }
.breakdown-row.is-weak .breakdown-score { color: #a3261f; }
.breakdown-row.is-ok .breakdown-score { color: #3f7a45; }
.breakdown-row a:hover .breakdown-title { color: var(--accent); }

.review { background: var(--tint); border-radius: var(--r-lg); padding: 26px; margin: 30px 0; }
.review ul { margin: 12px 0 0; padding-left: 20px; }
.review li { margin-bottom: 8px; }
.review a { color: var(--accent); }

/* ------------------------------------------------------- licence and cards */

/* The certificate has to read as credible at a glance, on a stranger's phone,
   outdoors - restrained and document-like, not celebratory (PRD §8). And it
   must never resemble a Danish state document (PRD §9.1). */
.cert-sheet { background: var(--white); border: 1px solid var(--line); border-radius: 24px; padding: 32px 26px; box-shadow: 0 2px 18px rgba(26, 24, 19, .05); }
.cert-sheet--compact { padding: 26px 22px; }
.cert-head { display: flex; align-items: center; gap: 12px; padding-bottom: 22px; border-bottom: 1px solid var(--line); margin-bottom: 26px; flex-wrap: wrap; }
.cert-brand { font-weight: 700; font-size: 17px; letter-spacing: -.02em; }
.cert-brand span { color: var(--muted); font-weight: 500; }
.cert-eyebrow { font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--accent); margin-top: 3px; }

.cert-status { font-family: var(--mono); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; padding: 6px 12px; border-radius: 999px; margin-left: auto; }
.cert-status--valid { background: #eef6ee; color: #2f6535; }
.cert-status--expired { background: var(--paper-3); color: var(--ink-soft); }
.cert-status--revoked { background: #fbeceb; color: #a3261f; }

.cert-label { font-family: var(--mono); font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.cert-name { font-size: clamp(28px, 6vw, 40px); font-weight: 700; letter-spacing: -.02em; margin: 6px 0 26px; line-height: 1.1; }

.cert-meta { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; margin: 0 0 26px; }
.cert-meta dt { font-family: var(--mono); font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); margin-bottom: 4px; }
.cert-meta dd { margin: 0; font-size: 16px; }

.cert-qr { text-align: center; padding: 24px 0 8px; border-top: 1px solid var(--line); }
.cert-qr img { margin: 0 auto 12px; }

/* Same visual weight as the holder's name is the requirement (PRD §9.1) - so
   this is a bordered block, not a footnote in grey 10px. */
.cert-disclaimer {
  margin-top: 24px; padding: 16px 18px; background: var(--paper-2);
  border-left: 3px solid var(--accent); border-radius: 0 var(--r) var(--r) 0;
  font-size: 14.5px; line-height: 1.6; color: var(--ink-soft);
}
.cert-identity { margin-top: 12px; font-size: 13px; color: var(--muted); line-height: 1.6; }

/* Plum ground, carrying the two-tone 'colour' variant of the CTCL mark
   (black wheels, white tick and T) - the variant made for exactly this, and
   the only one that holds up on a mid-tone. The downloadable card
   (CertificateRenderer) is drawn to match. */
.licence-card {
  background: var(--accent); color: #fff; border-radius: 24px; padding: 26px;
  width: 100%; max-width: 360px; box-shadow: 0 18px 44px rgba(50, 31, 44, .22);
}
.licence-card-top { display: flex; align-items: center; gap: 10px; margin-bottom: 26px; }
.licence-card-brand { font-weight: 700; font-size: 15px; }
.licence-card-brand span { opacity: .6; font-weight: 500; }
.licence-card-specimen { margin-left: auto; font-family: var(--mono); font-size: 9.5px; letter-spacing: .14em; text-transform: uppercase; border: 1px solid rgba(255, 255, 255, .4); border-radius: 999px; padding: 4px 9px; }
.licence-card-eyebrow { font-family: var(--mono); font-size: 9.5px; letter-spacing: .13em; text-transform: uppercase; opacity: .65; margin-bottom: 20px; }
.licence-card-label { font-family: var(--mono); font-size: 9px; letter-spacing: .14em; text-transform: uppercase; opacity: .6; }
.licence-card-name { font-size: 26px; font-weight: 700; letter-spacing: -.02em; margin: 5px 0 24px; }
.licence-card-meta { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.licence-card-mono { font-family: var(--mono); font-size: 13.5px; margin-top: 4px; }
.licence-card--preview .licence-card-name { opacity: .55; }

.verify-result { border: 1px solid var(--line); border-radius: var(--r-lg); padding: 26px; margin: 28px 0; }
.verify-result .cert-status { margin: 0 0 20px; display: inline-block; }

/* ----------------------------------------------------------- forms/account */

.form { margin: 32px 0; display: flex; flex-direction: column; gap: 22px; }
.field { display: flex; flex-direction: column; gap: 7px; }
.field-label { font-family: var(--mono); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.field input, .field select, .inline-form select {
  font-family: var(--sans); font-size: 17px; padding: 14px 16px; min-height: 52px;
  border: 1.5px solid var(--line); border-radius: var(--r); background: var(--white); color: var(--ink); width: 100%;
}
.field input:focus, .field select:focus { outline: none; border-color: var(--accent); }
.field input.mono { font-family: var(--mono); letter-spacing: .06em; text-transform: uppercase; }
.field-hint { font-size: 14px; color: var(--muted); line-height: 1.5; }

/* app.css carries `.form label { display: block; font-weight: 600; font-size:
   15px; margin-bottom: 6px }` for the parent site's contact form. Both of our
   label components live inside `.form`, and that selector is (0,1,1) - it
   out-specifies a bare class, so `.check`'s flex layout was never applied at
   all and the checkbox could not align with its text however the margin was
   tuned. These re-assert at matching specificity. */
.form label.field { display: flex; flex-direction: column; gap: 7px; margin-bottom: 0; font-weight: 400; font-size: inherit; }
.form label.check,
.check { display: flex; gap: 12px; align-items: flex-start; font-size: 15.5px; font-weight: 400; margin-bottom: 0; color: var(--ink-soft); line-height: 1.5; cursor: pointer; }
/* (line-height 23.25px - box 22px) / 2, so the box centres on the FIRST
   line of a label that wraps. */
.check input { width: 22px; height: 22px; flex: none; margin: 1px 0 0; accent-color: var(--accent); }
.check a { color: var(--accent); text-decoration: underline; }

.auth-or { text-align: center; position: relative; margin: 28px 0; color: var(--muted); font-family: var(--mono); font-size: 12px; letter-spacing: .1em; text-transform: uppercase; }
.auth-or::before { content: ''; position: absolute; left: 0; right: 0; top: 50%; height: 1px; background: var(--line); }
.auth-or span { background: var(--paper); padding: 0 14px; position: relative; }

.inline-form { margin: 16px 0 0; display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }

.attempt-list { list-style: none; padding: 0; margin: 0; }
.attempt-list li { display: flex; justify-content: space-between; gap: 16px; padding: 14px 4px; border-bottom: 1px solid var(--line); font-size: 15.5px; flex-wrap: wrap; }
.attempt-list .mono { color: var(--muted); font-size: 14px; }
.attempt-score.is-pass { color: #2f6535; }
.attempt-score.is-fail { color: #a3261f; }

.danger-zone { margin-top: 56px; padding: 26px; border: 1px solid #f0d6d4; border-radius: var(--r-lg); background: #fdf8f8; }
.danger-zone p { color: var(--ink-soft); font-size: 15.5px; margin: 10px 0 18px; }
.danger-zone .check { margin-bottom: 18px; }

/* ------------------------------------------------------------------ footer */

.foot { border-top: 1px solid var(--line); padding: 56px 0 40px; background: var(--paper-2); margin-top: 40px; }
.foot-grid { display: grid; gap: 34px; grid-template-columns: 1fr; }
.foot-grid > div { display: flex; flex-direction: column; gap: 10px; }
.foot-grid a { color: var(--ink-soft); font-size: 15.5px; min-height: 24px; }
.foot-grid a:hover { color: var(--accent); }
.foot-tagline { color: var(--muted); font-size: 15px; max-width: 30ch; margin-top: 12px; }
.foot-disclaimer { margin-top: 40px; padding-top: 24px; border-top: 1px solid var(--line); font-size: 13.5px; line-height: 1.65; color: var(--muted); max-width: 78ch; }
.foot-legal { margin-top: 18px; font-family: var(--mono); font-size: 11.5px; letter-spacing: .08em; color: var(--muted); }

/* Mid-attempt the nav is stripped server-side (focus mode in the layout), so
   there is nothing left to hide here.

   This used to be `body.is-testing .nav-cta { display: none }`, from when the
   test screen still rendered the full nav. Once focus mode replaced those links
   with a single "Stop test" button - which also carries .nav-cta - the rule
   started hiding the one control the screen has. It also still referenced
   .lang-switch, which no longer exists since the switcher became a dropdown. */
.nav-stop { white-space: nowrap; }

/* align-self matters: the footer puts the mark in a column flex container,
   whose default `align-items: stretch` pulled it to the column's full width
   while the height stayed fixed - a visibly squashed logo. `flex: none` alone
   does not prevent cross-axis stretching. */
.ctcl-mark {
  /* --mark-h comes from the partial's `height` prop; the fallback is only for
     a mark rendered without one. */
  height: var(--mark-h, 34px); width: auto; max-width: 100%;
  display: block; flex: none; align-self: flex-start; object-fit: contain;
}
.brand-text { white-space: nowrap; }
/* app.css pulls "CPH" 4px left, which all but closes the space after the
   wordmark. The mark now carries the identity, so the wordmark can breathe. */
.brand .sub, .cert-brand span, .licence-card-brand span { margin-left: 0; }
.foot-brand .ctcl-mark { margin-bottom: 4px; }

/* ------------------------------------------------------------- breakpoints */

@media (min-width: 700px) {
  .steps { grid-template-columns: repeat(2, 1fr); }
  .cards { grid-template-columns: repeat(3, 1fr); }
  .chapter-grid { grid-template-columns: repeat(2, 1fr); }
  .chapter-nav { grid-template-columns: 1fr 1fr; }
  .cert-sheet { padding: 42px 44px; }
  .cert-meta { grid-template-columns: repeat(3, 1fr); }
  .test-nav .btn { width: auto; }
  .foot-grid { grid-template-columns: 1.4fr 1fr 1fr 1fr; }
}

@media (min-width: 900px) {
  .steps { grid-template-columns: repeat(4, 1fr); }
  .chapter-grid { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 900px) {
  .nav nav { display: none; }
  .burger { display: flex; }

  /* The product name stays visible at every width: this subdomain IS the
     Copenhagen Tourist Cycling Licence, and a phone visitor arriving from a QR
     code has no other cue as to what they are looking at.

     It only fits because the CTA leaves. That action is already the first item
     in the burger menu, so nothing is lost by dropping the duplicate from a top
     bar with no room for it. */
  .nav .right .nav-cta { display: none; }
  .hero-grid { grid-template-columns: 1fr; }
  .panel-deep { margin-inline: 16px; }
}

@media print {
  .nav, .foot, .cta-row, .burger, .lang-switch { display: none !important; }
  .cert-sheet { border: 1px solid #ccc; box-shadow: none; }
}

/* consent banner (PRD §10) - only rendered when GA is configured */
.consent {
  position: fixed; left: 16px; right: 16px; bottom: 16px; z-index: 80;
  background: var(--accent-deep); color: var(--paper); border-radius: 18px;
  padding: 20px 22px; display: flex; flex-direction: column; gap: 16px;
  box-shadow: 0 16px 44px rgba(26, 24, 19, .3); max-width: 620px; margin-inline: auto;
}
.consent p { font-size: 15px; line-height: 1.55; color: rgba(255, 255, 255, .88); }
.consent a { color: var(--paper); text-decoration: underline; }
.consent-actions { display: flex; gap: 10px; justify-content: flex-end; flex-wrap: wrap; }
.consent .btn--ghost { color: var(--paper); border-color: rgba(255, 255, 255, .4); }
.consent .btn--ghost:hover { background: rgba(255, 255, 255, .12); color: var(--paper); }
.consent .btn:not(.btn--ghost) { background: var(--paper); border-color: var(--paper); color: var(--ink); }

/* the CTCL mark centred above the closing CTA */
.panel-mark { margin: 0 auto 22px; }

/* ------------------------------------------------- the pass, floating */

/* A drop shadow and a slow rise-and-fall, so the licence reads as an object
   sitting above the photo rather than a panel pasted onto it.

   6s and 10px of travel: enough to notice on a second look, not enough to
   distract while reading the headline next to it. The shadow animates with the
   card - a fixed shadow under a moving object is the thing that makes CSS
   float animations look wrong. */
@keyframes ctcl-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-10px); }
}

.hero-card-float .licence-card {
  animation: ctcl-float 6s ease-in-out infinite;
  will-change: transform;
  box-shadow:
    0 18px 36px rgba(26, 24, 19, .34),
    0 42px 80px rgba(26, 24, 19, .30);
}

/* Motion here is decorative and unrequested, so it is the first thing to go. */
@media (prefers-reduced-motion: reduce) {
  .hero-card-float .licence-card { animation: none; }
}

/* ------------------------------------------ mobile nav: brand must shrink */

/* app.css sets `.brand { white-space: nowrap; flex-shrink: 0 }`, which was
   fine for the parent's 32px Wheel Tile. The CTCL mark is landscape and wider,
   and with the language switcher and the CTA beside it the row stopped fitting
   a 390px viewport - pushing the whole page into horizontal overflow, so every
   line of the hero was clipped on the right.
   Below 560px the wordmark goes and the mark carries the brand on its own. */
@media (max-width: 700px) {
  .brand { min-width: 0; flex-shrink: 1; align-items: flex-start; gap: 9px; }
  .brand .ctcl-mark { --mark-h: 30px; margin-top: 1px; }
  .brand-text { font-size: 15px; }

  /* Wrapped, not shrunk: on one line the name would need ~8px to fit a 390px
     viewport, which is unreadable. */
  .brand-qualifier {
    white-space: normal; font-size: 9px;
    letter-spacing: .09em; line-height: 1.35; max-width: 136px;
  }

  .nav .row { height: auto; min-height: 64px; padding-block: 10px; }
  .nav .right { gap: 8px; }
}

@media (max-width: 430px) {
  .brand-text { font-size: 14px; }
  .brand-qualifier { font-size: 8.5px; max-width: 124px; }
}

/* The pass sits inside the gallery, so on a phone it has the gallery's width
   minus its padding to work with - about 300px. At the desktop sizes the
   certificate number wraps onto two lines, which makes the specimen look
   broken rather than small. */
@media (max-width: 560px) {
  .hero-card-float { padding: 16px; }
  .hero-card-float .licence-card { max-width: 94%; padding: 20px; }
  .licence-card-top { margin-bottom: 18px; }
  .licence-card-eyebrow { margin-bottom: 14px; }
  .licence-card-name { font-size: 22px; margin: 4px 0 18px; }
  .licence-card-meta { gap: 12px; }
  .licence-card-mono { font-size: 12px; }
}

/* =========================================================================
   The guided journey (stepper / wizard)
   ========================================================================= */

/* The four stages, on every page that belongs to one. Horizontal rail on
   desktop; on a phone it scrolls sideways rather than wrapping, because a
   stepper that wraps to two lines stops reading as a sequence. */
.journey {
  border-bottom: 1px solid var(--line);
  background: var(--paper-2);
  --journey-dot: 30px;
  --journey-pad: 16px;
}
.journey ol {
  display: flex; list-style: none; margin: 0 auto; padding: 0 24px;
  max-width: var(--maxw); gap: 0; overflow-x: auto; scrollbar-width: none;
}
.journey ol::-webkit-scrollbar { display: none; }

.journey-step { flex: 1 1 0; min-width: 96px; position: relative; }

/* Dot above label, not beside it.
   The connector has to run through the row of dots, and with the label on the
   same horizontal axis the line struck straight through the words - on a phone
   "Learn" and "Watch" rendered with what looked like strikethrough. Stacking
   puts the text below the line, so it cannot be crossed at any width. */
.journey-step a {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: var(--journey-pad) 8px; text-align: center; color: var(--muted);
}

/* Spans centre-of-previous-dot to centre-of-this-dot; both ends are covered by
   the opaque dots themselves. */
.journey-step + .journey-step::before {
  content: ''; position: absolute; z-index: 0;
  top: calc(var(--journey-pad) + (var(--journey-dot) / 2));
  left: -50%; width: 100%; height: 1.5px; background: var(--line);
}
.journey-step.is-past::before,
.journey-step.is-current::before { background: var(--accent); }

.journey-dot {
  position: relative; z-index: 1; flex: none;
  width: var(--journey-dot); height: var(--journey-dot); border-radius: 999px;
  display: grid; place-items: center;
  background: var(--paper); border: 1.5px solid var(--line);
  font-family: var(--mono); font-size: 12px; color: var(--muted);
  transition: background .2s ease, border-color .2s ease, color .2s ease;
}
.journey-tick { display: none; }
.journey-label { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.journey-name { font-size: 13.5px; font-weight: 600; color: var(--ink-soft); white-space: nowrap; }
.journey-sub {
  font-family: var(--mono); font-size: 10px; letter-spacing: .06em;
  color: var(--muted); white-space: nowrap;
}

.journey-step.is-current .journey-dot {
  background: var(--accent); border-color: var(--accent); color: #fff;
}
.journey-step.is-current .journey-name { color: var(--accent); }

.journey-step.is-done .journey-dot {
  background: #2f6535; border-color: #2f6535; color: #fff;
}
.journey-step.is-done .journey-num { display: none; }
.journey-step.is-done .journey-tick { display: block; }
.journey-step.is-done .journey-name { color: var(--ink); }

.journey-step a:hover .journey-name { color: var(--accent); }

/* ---- Micro progress within a stage */

.step-head {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 16px; margin-bottom: 10px; flex-wrap: wrap;
}
.step-time {
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted);
}
.step-track {
  height: 4px; background: var(--paper-3); border-radius: 999px;
  overflow: hidden; margin-bottom: 30px;
}
.step-fill { height: 100%; background: var(--accent); border-radius: 999px; transition: width .4s ease; }

.chapter-body { padding-top: 40px; }

/* ---- The one way forward, at the end of every step */

.step-nav {
  margin: 48px 0 20px; padding: 30px; border-radius: var(--r-lg);
  background: var(--tint); text-align: center;
}
.step-next-label {
  font-size: 15.5px; color: var(--ink-soft); margin-bottom: 16px;
}
.step-next-label strong { color: var(--ink); font-weight: 600; }
.step-cta { min-width: min(320px, 100%); justify-content: center; }
.step-nav-aside {
  display: flex; gap: 20px; justify-content: center; flex-wrap: wrap;
  margin-top: 20px; font-size: 14.5px;
}
.step-nav-aside a { color: var(--muted); }
.step-nav-aside a:hover { color: var(--accent); }

.more-link { color: var(--accent); font-size: 15.5px; text-decoration: underline; text-underline-offset: 3px; }

/* ---- Course index CTA */

.course-intro { padding-bottom: 8px; }
.course-intro + .divide { padding-top: 36px; border-top: 0; }
.course-cta { margin-top: 32px; }
.course-cta .btn { min-width: min(340px, 100%); justify-content: center; }
.course-cta .cap { margin-top: 12px; }
.course-cta.is-complete .btn { background: #2f6535; border-color: #2f6535; }

/* ---- Chapter rows, now with thumbnails */

.chapter-row { grid-template-columns: auto 1fr auto auto; }
.chapter-row-media {
  position: relative; flex: none; width: 92px; height: 66px;
  border-radius: 12px; overflow: hidden; background: var(--paper-3);
}
.chapter-row-media img { width: 100%; height: 100%; object-fit: cover; }
/* Solid white on solid plum. The base `.chapter-num` is a mono label at 12px
   with .14em tracking - fine as an eyebrow, but inside a 24px badge it rendered
   thin, off-centre and washed out against the photo behind it. */
.chapter-row .chapter-row-media .chapter-num {
  position: absolute; left: 5px; bottom: 5px;
  width: 26px; height: 26px; border-radius: 999px;
  background: var(--accent); color: #ffffff;
  display: grid; place-items: center;
  font-family: var(--sans); font-size: 11.5px; font-weight: 700;
  letter-spacing: 0; line-height: 1;
  box-shadow: 0 2px 8px rgba(26, 24, 19, .45);
}
.chapter-row-read {
  display: grid; place-items: center; width: 26px; height: 26px;
  border-radius: 999px; background: #2f6535; color: #fff;
}
.chapter-row.is-read .chapter-row-media img { opacity: .55; }

/* ---- Sticky mobile action bar */

.stickybar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 70;
  display: none; align-items: center; gap: 14px;
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--paper) 92%, transparent);
  backdrop-filter: blur(12px); border-top: 1px solid var(--line);
  transform: translateY(110%); transition: transform .25s ease;
}
.stickybar.is-visible { transform: translateY(0); }
.stickybar-label {
  flex: 1; min-width: 0; font-size: 13.5px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.stickybar-cta { flex: none; white-space: nowrap; }

@media (max-width: 760px) {
  .stickybar { display: flex; }
  /* Room for the bar, so it never sits over the last line of the page. */
  body.has-stickybar .foot { padding-bottom: 96px; }
}

@media (max-width: 640px) {
  .journey { --journey-dot: 26px; --journey-pad: 13px; }
  .journey ol { padding-inline: 12px; }
  .journey-step { min-width: 78px; }
  .journey-sub { display: none; }
  .journey-name { font-size: 12.5px; }
  .step-nav { padding: 22px 18px; }
  .chapter-row-media { width: 72px; height: 54px; }
}

@media (prefers-reduced-motion: reduce) {
  .stickybar, .step-fill { transition: none; }
}

/* =========================================================================
   Landing page - photo-led
   ========================================================================= */

/* Steps as picture cards. The number sits on the photo rather than above the
   heading, which buys back the vertical space the image costs. */
.step { padding: 0; overflow: hidden; }
.step-media {
  position: relative; aspect-ratio: 4 / 3; background: var(--paper-3);
  border-radius: var(--r-lg) var(--r-lg) 0 0; overflow: hidden;
}
.step-media img { width: 100%; height: 100%; object-fit: cover; }
/* A solid badge, not text with a shadow. Over photography the shadowed number
   was legible on the dark images and nearly invisible on the bright ones - and
   which photo sits behind which step is not something the CSS can know. */
.step-media .step-num {
  position: absolute; left: 14px; bottom: 12px;
  width: 34px; height: 34px; border-radius: 999px;
  background: var(--accent); color: #ffffff;
  display: grid; place-items: center;
  font-family: var(--sans); font-size: 13px; font-weight: 700;
  letter-spacing: 0; line-height: 1;
  box-shadow: 0 3px 10px rgba(26, 24, 19, .35);
}
.step h3 { margin: 18px 22px 6px; }
.step p { margin: 0 22px 24px; }

.steps-cta { margin-top: 40px; text-align: center; }
.steps-cta .cap { margin-top: 12px; }

/* Full-bleed photo band. 100vw off a centred container without a horizontal
   scrollbar - margin-inline the difference between the viewport and the
   container, rather than 100vw width which includes the scrollbar. */
.band {
  position: relative; padding: 0;
  margin-inline: calc(50% - 50vw); width: 100vw; max-width: 100vw;
  height: clamp(220px, 34vw, 420px); overflow: hidden;
}
.band img { width: 100%; height: 100%; object-fit: cover; }
.band-caption {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 40px 24px 20px; margin: 0 auto; max-width: var(--maxw);
  font-family: var(--mono); font-size: 12px; letter-spacing: .1em;
  text-transform: uppercase; color: #fff;
  background: linear-gradient(to top, rgba(26, 24, 19, .72), transparent);
}

/* Chapter cards with photography */
.chapter-card { padding: 0; overflow: hidden; }
.chapter-card-media {
  position: relative; display: block; aspect-ratio: 3 / 2;
  background: var(--paper-3); overflow: hidden;
}
.chapter-card-media img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform .4s ease;
}
.chapter-card:hover .chapter-card-media img { transform: scale(1.04); }
.chapter-card-media .chapter-num {
  position: absolute; left: 14px; bottom: 11px;
  width: 32px; height: 32px; border-radius: 999px;
  background: var(--accent); color: #ffffff;
  display: grid; place-items: center;
  font-family: var(--sans); font-size: 12px; font-weight: 700;
  letter-spacing: 0; line-height: 1;
  box-shadow: 0 3px 10px rgba(26, 24, 19, .35);
}
.chapter-card h3 { margin: 18px 20px 6px; }
.chapter-card p { margin: 0 20px 22px; }

@media (prefers-reduced-motion: reduce) {
  .chapter-card:hover .chapter-card-media img { transform: none; }
}

/* ---- Subtitle picker (the film ships in 9 languages) */
.subs { margin: 30px 0 18px; }
.subs-label {
  font-family: var(--mono); font-size: 11px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 12px;
}
.subs-list { display: flex; flex-wrap: wrap; gap: 8px; }
.subs-flag {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 14px; min-height: 44px; cursor: pointer;
  background: var(--white); border: 1.5px solid var(--line); border-radius: 999px;
  font-family: var(--sans); font-size: 14.5px; color: var(--ink-soft);
  transition: border-color .15s ease, background .15s ease;
}
.subs-flag:hover { border-color: var(--muted); }
.subs-flag.is-active { border-color: var(--accent); background: var(--tint); color: var(--accent); }
.subs-emoji { font-size: 17px; line-height: 1; }
/* The "Spoken English" / "German subtitles" qualifier is useful on the one
   English row and noise on the other eight, so it only shows on the active
   choice and on wider screens. */
.subs-note { display: none; font-family: var(--mono); font-size: 10.5px; color: var(--muted); }
.subs-flag.is-active .subs-note { color: var(--accent); }
@media (min-width: 700px) {
  .subs-flag.is-active .subs-note { display: inline; }
}

/* ---- Chapter detail: rules, fines, sources */

.rules { margin: 44px 0; }
.rules-head, .fines-head { font-size: clamp(20px, 2.8vw, 25px); margin-bottom: 20px; letter-spacing: -.012em; }
.rules-list { list-style: none; counter-reset: rule; padding: 0; margin: 0; }
.rules-list li {
  counter-increment: rule; position: relative;
  padding: 18px 0 18px 46px; border-bottom: 1px solid var(--line);
}
.rules-list li::before {
  content: counter(rule); position: absolute; left: 0; top: 20px;
  width: 28px; height: 28px; border-radius: 999px;
  background: var(--tint); color: var(--accent);
  font-family: var(--mono); font-size: 12px;
  display: grid; place-items: center;
}
.rules-list h3 { font-size: 17.5px; margin-bottom: 5px; }
.rules-list p { color: var(--ink-soft); font-size: 16px; }

.fines { margin: 44px 0; padding: 26px; border-radius: var(--r-lg); background: var(--paper-2); }
.fines table { width: 100%; border-collapse: collapse; }
.fines th {
  text-align: left; font-weight: 500; font-size: 15.5px; color: var(--ink-soft);
  padding: 12px 0; border-bottom: 1px solid var(--line);
}
.fines td {
  text-align: right; font-family: var(--mono); font-size: 15px; white-space: nowrap;
  padding: 12px 0 12px 16px; border-bottom: 1px solid var(--line); color: var(--ink);
}
.fines tr:last-child th, .fines tr:last-child td { border-bottom: 0; }
/* The "as of" date is the honest part of quoting a fine, so it is not a
   footnote in 10px grey. */
.fines-note { margin-top: 16px; font-size: 14px; line-height: 1.55; color: var(--muted); }

.sources { margin: 44px 0 12px; padding-top: 26px; border-top: 1px solid var(--line); }
.sources ul { list-style: none; padding: 0; margin: 12px 0 0; }
.sources li { font-size: 15px; color: var(--ink-soft); padding: 5px 0; }
.sources li::before { content: '\2014'; color: var(--muted); margin-right: 9px; }
.sources-note { margin-top: 16px; font-size: 14.5px; color: var(--muted); }
.sources-note a { color: var(--accent); }

/* ---- Language dropdown */

.lang { position: relative; }
.lang summary {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  padding: 8px 11px; min-height: 40px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--white);
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .07em;
  color: var(--ink-soft); list-style: none;
}
.lang summary::-webkit-details-marker { display: none; }
.lang summary::marker { content: ''; }
.lang summary:hover { border-color: var(--muted); }
.lang[open] summary { border-color: var(--accent); color: var(--accent); }
.lang-flag { font-size: 15px; line-height: 1; }
.lang-caret { transition: transform .18s ease; }
.lang[open] .lang-caret { transform: rotate(180deg); }

.lang-menu {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 90;
  min-width: 184px; padding: 6px;
  background: var(--white); border: 1px solid var(--line); border-radius: 14px;
  box-shadow: 0 14px 36px rgba(26, 24, 19, .14);
}
.lang-menu a {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; min-height: 42px; border-radius: 9px;
  font-size: 15px; color: var(--ink-soft);
}
.lang-menu a:hover { background: var(--paper-2); }
.lang-menu a[aria-current="true"] { color: var(--accent); font-weight: 600; }
.lang-native { flex: 1; }

@media (prefers-reduced-motion: reduce) { .lang-caret { transition: none; } }

/* ---- Skip-the-course warning on the test screen */

.skip-warn {
  margin: 30px 0; padding: 26px; border-radius: var(--r-lg);
  background: #fdf3e7; border: 1px solid #f0dcc0;
}
.skip-warn h2 { font-size: clamp(19px, 2.6vw, 23px); margin-bottom: 10px; }
.skip-warn p { color: var(--ink-soft); font-size: 16px; }
.skip-missing { list-style: none; padding: 0; margin: 16px 0 20px; }
.skip-missing li {
  display: flex; align-items: center; gap: 9px;
  font-size: 15px; color: #8a5a1c; padding: 4px 0;
}
.skip-missing li::before { content: '!'; flex: none;
  width: 20px; height: 20px; border-radius: 999px; background: #c8791f; color: #fff;
  font-family: var(--mono); font-size: 12px; display: grid; place-items: center; }
.skip-actions { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
.skip-anyway {
  background: none; border: 0; padding: 10px 4px; cursor: pointer;
  font-family: var(--sans); font-size: 15px; color: var(--muted);
  text-decoration: underline; text-underline-offset: 3px; min-height: 44px;
}
.skip-anyway:hover { color: var(--accent); }

/* ---- About the parent project */

.about-parent { background: var(--paper-2); }
.about-grid { display: grid; gap: 34px; grid-template-columns: 1fr; align-items: center; }
.about-media { border-radius: var(--r-lg); overflow: hidden; aspect-ratio: 3 / 2; background: var(--paper-3); }
.about-media img { width: 100%; height: 100%; object-fit: cover; }
.about-body h2 { font-size: clamp(25px, 3.6vw, 36px); margin: 12px 0 16px; letter-spacing: -.015em; }
.about-body p { color: var(--ink-soft); margin-bottom: 16px; }
.about-links { list-style: none; padding: 0; margin: 22px 0; display: flex; flex-wrap: wrap; gap: 8px; }
.about-links a {
  display: inline-block; padding: 8px 14px; border-radius: 999px;
  background: var(--white); border: 1px solid var(--line);
  font-size: 14.5px; color: var(--ink-soft);
}
.about-links a:hover { border-color: var(--accent); color: var(--accent); }
.foot-primary { font-weight: 600; color: var(--accent) !important; }

@media (min-width: 860px) {
  .about-grid { grid-template-columns: 1fr 1.15fr; gap: 48px; }
}

/* ---- Confirmation dialog (account deletion) */

.modal {
  border: 0; border-radius: 20px; padding: 30px;
  max-width: 480px; width: calc(100% - 32px);
  background: var(--white); color: var(--ink);
  box-shadow: 0 30px 80px rgba(26, 24, 19, .3);
}
.modal::backdrop { background: rgba(26, 24, 19, .55); }
.modal h2 { font-size: 23px; margin-bottom: 12px; letter-spacing: -.015em; }
.modal p { color: var(--ink-soft); font-size: 16px; line-height: 1.55; }
.modal-actions {
  display: flex; gap: 10px; justify-content: flex-end; flex-wrap: wrap; margin-top: 26px;
}
.danger-zone .field { margin-top: 18px; }
.danger-zone .btn--danger { margin-top: 22px; }

/* ---- Discounts / what the licence is good for */
.perks { background: var(--tint); }
.perks-grid { display: grid; gap: 34px; grid-template-columns: 1fr; align-items: center; }
.perks-body h2 { font-size: clamp(25px, 3.6vw, 36px); margin: 12px 0 16px; letter-spacing: -.015em; }
.perks-body p { color: var(--ink-soft); margin-bottom: 16px; }
.perks-id {
  background: var(--white); border-radius: var(--r-lg);
  padding: 22px 24px; margin: 22px 0 18px;
}
.perks-id h3 { font-size: 17.5px; margin-bottom: 8px; }
.perks-id p { margin-bottom: 0; font-size: 15.5px; }
.perks-media { border-radius: var(--r-lg); overflow: hidden; aspect-ratio: 3 / 2; background: var(--paper-3); }
.perks-media img { width: 100%; height: 100%; object-fit: cover; }

@media (min-width: 860px) {
  .perks-grid { grid-template-columns: 1.15fr 1fr; gap: 48px; }
}

/* ---- /login/sent */

.sent .eyebrow { margin-top: 34px; }
.sent h1 { margin-bottom: 18px; }
.sent .lede { margin-inline: auto; }
.sent-image {
  width: 100%; max-width: 460px; margin: 0 auto;
  border-radius: var(--r-lg); aspect-ratio: 3 / 2; object-fit: cover;
}
.sent-spam { margin-top: 26px; }
/* The form is a block, so the button inside it needs the centring - a
   text-align on the parent does not reach a flex-displayed .btn. */
.sent-resend { margin-top: 34px; display: flex; justify-content: center; }

/* ---- /account: room to breathe between sections */

.account-status { margin-top: 42px; }
.account-identity { margin-bottom: 8px; }
.account-lang-hint { margin-top: 10px; }
.account-help { margin-top: 46px; }
.account-help p { color: var(--ink-soft); }
.account-help a { color: var(--accent); text-decoration: underline; }
.account-status .cert-meta { margin-bottom: 18px; }
.attempt-list { margin-bottom: 8px; }
.danger-zone { margin-top: 56px; padding: 26px 26px 30px; }
/* The heading carries its own top margin from .sub-head; inside the box that
   stacked on top of the box's padding and left a large empty band. */
.danger-zone .sub-head { margin-top: 0; }
.account-help .sub-head,
.account-status .sub-head { margin-top: 0; }

/* Custom caret: `appearance: none` removes the native arrow, which sat at the
   far right of a full-width control and read as unrelated to the value. */
.field select,
.inline-form select {
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%238c8678' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 16px center;
  background-size: 11px 7px;
  padding-right: 44px;
}
.inline-form select { width: auto; min-width: 220px; }

/* ---- /verify: the QR scanner */

.scan { margin: 30px 0 8px; }
.scan-start { gap: 10px; }
.scan-stage { margin-top: 4px; }
.scan-stage video {
  width: 100%; max-width: 420px; aspect-ratio: 1;
  border-radius: var(--r-lg); background: var(--ink); object-fit: cover; display: block;
}
.scan-stage .cap { margin: 12px 0 14px; }
.scan-msg { margin-top: 16px; }
.scan-unsupported { margin-top: 4px; max-width: 52ch; }

/* Input and button stacked, not floating apart. .form's column gap was pushing
   the submit a long way below the field it belongs to. */
/* Stacked, not a row. The field previously carried `flex: 1 1 220px` for a
   side-by-side layout - in a column that basis becomes a 220px HEIGHT, which
   is what pushed the submit button a long way below its input. */
.verify-form { display: flex; flex-direction: column; gap: 14px; align-items: stretch; margin-top: 26px; }
.verify-form .field { flex: 0 0 auto; margin: 0; }
.verify-form .btn { align-self: flex-start; }

/* ---- Focused test mode: chrome stripped back to a single exit */

body.is-focus .nav .right { margin-left: auto; }
body.is-focus .support-ribbon { display: none; }

/* ---- Footer colophon: the Bike Safe CPH sign-off */

.colophon {
  display: grid; gap: 18px; grid-template-columns: 1fr;
  margin-top: 30px; padding-top: 26px; border-top: 1px solid var(--line);
}
.colophon-brand {
  display: inline-flex; align-items: center; gap: 10px;
  font-weight: 700; font-size: 15.5px; color: var(--ink);
  align-self: start;
}
.colophon-brand img { width: 36px; height: 36px; border-radius: 9px; display: block; }
.colophon-brand .sub { color: var(--muted); font-weight: 500; }
.colophon-brand:hover { color: var(--accent); }

.colophon-text p {
  font-size: 14px; line-height: 1.65; color: var(--ink-soft); margin-bottom: 8px;
  max-width: 82ch;
}
.colophon-text a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.colophon-legal { font-size: 12.5px !important; color: var(--muted) !important; }
.colophon-legal a { color: var(--muted); }

@media (min-width: 760px) {
  .colophon { grid-template-columns: auto 1fr; gap: 26px; align-items: start; }
}

/* ---- /account spacing */

/* The heading block uses .sec-head like every other page; these give the
   sections below it the same rhythm. `.cta-row` only gets a top margin inside
   .hero (app.css), so it needs one here too. */
.account-sheet { margin-top: 34px; }
section > .wrap > .sec-head + .cert-sheet,
section > .wrap > .sec-head + .cta-row { margin-top: 34px; }
.account-status .cert-meta { margin-bottom: 18px; }

/* ---- Heading blocks: eyebrow / h1 / lede
   app.css sets `.sec-head { gap: 14px }`, which is tight once the h1 uses a
   1.02 line-height - the three lines read as one clump. This is the shared
   component, so raising it here spaces the heading of every page at once. */
.sec-head { gap: 20px; }
.sec-head .lede { margin-top: 2px; }
.sec-head h1 + .lede,
.sec-head h2 + .lede { margin-top: 0; }

/* ---- Button rows

   app.css scopes `.cta-row { margin-top: 30px }` to `.hero`, so every other
   button row on the site sat flush against whatever preceded it. That has now
   been reported separately on the closing CTA panel, the account page and the
   result screen - so it is fixed once here rather than a fourth time.
   `.panel-deep .cta-row` and the `.sec-head +` rule are more specific and keep
   their larger values. */
.cta-row {
  /* app.css only makes this a flex row inside .hero, so everywhere else the
     buttons were bare inline-flex boxes separated by a text space - which is
     why adjacent buttons sat almost touching. */
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 16px;
  margin-top: 30px;
}
.cta-row:first-child { margin-top: 0; }
/* Keep the hero in step - app.css sets 14px there specifically. */
.hero .cta-row { gap: 16px; }



/* ---- One container, constrained content

   Every page now uses the homepage's `.wrap` (1200px), so content starts at the
   same left edge everywhere. Readability is handled per element instead of by
   narrowing the page: a 1152px input or an 1152px line of prose is unusable,
   but the fix belongs on the input and the paragraph, not on the container. */

/* app.css puts `max-width: 68ch` on .prose itself. Where .prose sits ON the
   wrap that shrank and re-centred the whole container, pulling those pages out
   of line with the rest of the site. Measure the children instead. */

/* Reading and input measures. */
.form { max-width: 560px; }
.verify-form { max-width: 560px; }
.faq { max-width: 76ch; }
.cert-sheet { max-width: 760px; }
.rules-list, .fines, .sources, .tested, .more-detail { max-width: 76ch; }
.step-nav { max-width: 76ch; }
.breakdown, .review, .scorecard, .test-track--lg { max-width: 76ch; }
.rules-head, .fines-head { max-width: 76ch; }
.account-status, .account-identity, .account-help, .attempt-list, .danger-zone { max-width: 76ch; }
.sent { max-width: 62ch; margin-inline: auto; }

/* The test screen keeps a tight measure: a question that runs the full width
   is slower to read, and the options become hard to scan. */
/* The test screen shares the site column (see .wrap.narrow) rather than a
   measure of its own, so moving from a chapter into the test does not shift
   the content sideways. */
.test-q, .test-options, .test-bar, .test-track, .test-nav { max-width: none; }

/* Centred pages centre their content within the shared container rather than
   narrowing it. */
.wrap.center > * { margin-inline: auto; }

/* ---- Chapter column aligned to the stepper

   The chapter text lines up with the first and last stepper dots, so the rail
   above reads as part of the same document. Four steps share the rail's inner
   width, so the dot centres sit at 12.5% and 87.5% - a 75% span. Both the rail
   and the column use --maxw and the same 24px padding, so this holds at every
   width, not just at 1200. */
/* Every page except the home page shares this column, and it is set by the
   stepper: four steps span the rail's inner width, so the first and last dot
   centres sit at 12.5% and 87.5% - a 75% span. On the course pages the text
   therefore lines up with the rail above it, and everywhere else the site keeps
   one consistent measure instead of a different one per template.

   Both the rail and the column use --maxw and the same 24px padding, so this
   holds at any viewport, not just at 1200. */
@media (min-width: 900px) {
  .wrap.narrow {
    max-width: calc((var(--maxw) - 48px) * 0.75 + 48px);
  }
}

/* ---- Change the name on the licence */
.account-edit {
  display: block; margin-top: 4px; font-size: 13.5px;
  color: var(--accent); text-decoration: underline; text-underline-offset: 2px;
}
.name-effect { margin-top: 44px; max-width: 76ch; }
.name-effect ul { list-style: none; padding: 0; margin: 0; }
.name-effect li {
  position: relative; padding: 9px 0 9px 24px;
  color: var(--ink-soft); font-size: 15.5px; line-height: 1.6;
  border-bottom: 1px solid var(--line);
}
.name-effect li:last-child { border-bottom: 0; }
.name-effect li::before {
  content: ''; position: absolute; left: 4px; top: 18px;
  width: 6px; height: 6px; border-radius: 999px; background: var(--accent);
}
.field input:disabled { background: var(--paper-2); color: var(--muted); cursor: not-allowed; }

/* ---- Read aloud */
.speak { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 22px; }
.speak-btn { font-size: 14.5px; padding: 10px 18px; min-height: 44px; gap: 8px; }
.speak-status {
  font-family: var(--mono); font-size: 11px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--accent);
}
/* A quiet pulse while it is talking, so the state is visible without a big
   animated bar competing with the text being read. */
.speak[data-state="playing"] .speak-status::before {
  content: ''; display: inline-block; width: 7px; height: 7px; margin-right: 7px;
  border-radius: 999px; background: var(--accent); animation: speak-pulse 1.4s ease-in-out infinite;
}
@keyframes speak-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) {
  .speak[data-state="playing"] .speak-status::before { animation: none; }
}

/* The chapter number in front of its title, so the sequence is readable
   without relying on a small badge over photography. */
.chapter-row-num {
  font-family: var(--mono); font-size: 13px; color: var(--accent);
  margin-right: 9px; font-weight: 500;
}
.chapter-row.is-read .chapter-row-num { color: #2f6535; }

/* app.css puts the reading measure on `.prose` itself. That caps the headings
   too, so the legal pages sat narrower than every other page. Move it to the
   text: headings span the shared column, paragraphs keep a sane line length. */
.wrap.narrow > .prose { max-width: none; }
.wrap.narrow > .prose > p,
.wrap.narrow > .prose > ul,
.wrap.narrow > .prose > ol { max-width: 68ch; }

/* ---- Name-change disclosure on the certificate */
.cert-namechange {
  margin-top: 18px; padding: 12px 16px; border-radius: var(--r);
  background: #fdf3e7; border-left: 3px solid #c8791f;
  font-size: 14.5px; line-height: 1.55; color: #7a4f13;
}

/* The CTA in the open mobile menu.

   app.css styles every link in the drawer with `.mobile-menu a { color:
   var(--ink-soft) }` (0,1,1), which out-specifies `.btn`'s own white (0,1,0) -
   so the one filled button in the menu came out with dark text on the plum and
   barely read as a button at all. Matching both classes takes it to (0,2,0). */
.mobile-menu a.btn:not(.btn--ghost),
.mobile-menu a.btn:not(.btn--ghost):hover,
.mobile-menu a.btn:not(.btn--ghost):focus { color: #fff; }

/* ---- Holographic foil on the licence card

   A slow diagonal sheen over the plum, the way a security hologram catches the
   light as you tilt it. Purely decorative, and deliberately restrained: the
   card is a credential, so this should read as a finish on the material rather
   than a graphic sitting on top of it.

   `background-clip: padding-box` on the sheen keeps it inside the rounded
   corners without a second overflow context. */
.licence-card { position: relative; overflow: hidden; isolation: isolate; }

.licence-card::before {
  content: ''; position: absolute; inset: -40%; z-index: 0; pointer-events: none;
  background: linear-gradient(
    115deg,
    transparent 30%,
    rgba(255, 255, 255, .03) 42%,
    rgba(255, 214, 235, .11) 47%,
    rgba(190, 235, 255, .13) 50%,
    rgba(214, 255, 224, .11) 53%,
    rgba(255, 255, 255, .03) 58%,
    transparent 70%
  );
  animation: card-sheen 16s ease-in-out infinite;
}

/* A fine diagonal grating - what actually makes foil read as foil rather than
   as a gradient. Kept very low contrast so text over it stays crisp. */
.licence-card::after {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: repeating-linear-gradient(
    115deg,
    rgba(255, 255, 255, .03) 0 1px,
    transparent 1px 6px
  );
  mix-blend-mode: overlay;
}

/* Everything else sits above the foil. */
.licence-card > * { position: relative; z-index: 1; }

/* A drift rather than a sweep. The earlier version travelled 76% of the card
   in 7s, which reads as an animation playing on a credential - the eye tracks
   it and it looks like a loading state. At 22% total travel over 16s it only
   registers as the surface catching the light.

   The opacity ramp matters as much as the distance: at constant opacity the
   reversal at each end is visible as a bounce, and a hologram does not bounce.
   Fading to nothing at the turnarounds hides them. */
@keyframes card-sheen {
  0%   { transform: translateX(-11%) rotate(0.001deg); opacity: 0; }
  25%  { opacity: 1; }
  50%  { transform: translateX(11%) rotate(0.001deg); opacity: 0; }
  75%  { opacity: 1; }
  100% { transform: translateX(-11%) rotate(0.001deg); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .licence-card::before,
  .certificate .cert-sheet::before { animation: none; opacity: .5; }
}

/* =========================================================================
   The certificate page - the card, at full size

   /l/* is the thing people are shown, so it should look like the object it
   represents rather than a page describing it: plum ground, the same
   holographic finish as the downloadable card, the QR on a white panel because
   a scanner needs the contrast.
   ========================================================================= */

.certificate .cert-sheet {
  position: relative; overflow: hidden; isolation: isolate;
  background: var(--accent); border-color: transparent; color: #fff;
  box-shadow: 0 24px 60px rgba(50, 31, 44, .28);
}

/* Same foil as .licence-card, at the larger scale of a full page. */
.certificate .cert-sheet::before {
  content: ''; position: absolute; inset: -40%; z-index: 0; pointer-events: none;
  background: linear-gradient(
    115deg,
    transparent 30%,
    rgba(255, 255, 255, .03) 42%,
    rgba(255, 214, 235, .10) 47%,
    rgba(190, 235, 255, .12) 50%,
    rgba(214, 255, 224, .10) 53%,
    rgba(255, 255, 255, .03) 58%,
    transparent 70%
  );
  animation: card-sheen 20s ease-in-out infinite;
}
.certificate .cert-sheet::after {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: repeating-linear-gradient(115deg, rgba(255, 255, 255, .028) 0 1px, transparent 1px 7px);
  mix-blend-mode: overlay;
}
.certificate .cert-sheet > * { position: relative; z-index: 1; }

.certificate .cert-head { border-bottom-color: rgba(255, 255, 255, .22); }
.certificate .cert-brand { color: #fff; }
.certificate .cert-brand span { color: rgba(255, 255, 255, .62); }
.certificate .cert-eyebrow { color: rgba(255, 255, 255, .72); }
.certificate .cert-label { color: rgba(255, 255, 255, .62); }
.certificate .cert-name { color: #fff; }
.certificate .cert-meta dt { color: rgba(255, 255, 255, .6); }
.certificate .cert-meta dd { color: #fff; }

/* The QR keeps a white panel: scanners want the contrast, and a plum-on-plum
   code is a code that does not read. */
.certificate .cert-qr {
  border-top: 0; margin-top: 8px; padding: 24px;
  background: #fff; border-radius: 18px;
}
.certificate .cert-qr .cap { color: var(--muted); margin-bottom: 0; }

/* The disclaimer has to hold its visual weight on the dark ground too
   (PRD §9.1) - a translucent panel rather than faint text. */
.certificate .cert-disclaimer {
  background: rgba(26, 24, 19, .28); border-left-color: rgba(255, 255, 255, .5);
  color: rgba(255, 255, 255, .95);
}
.certificate .cert-identity { color: rgba(255, 255, 255, .72); }
.certificate .cert-namechange {
  background: rgba(26, 24, 19, .3); border-left-color: #f0b46a; color: #ffe6c4;
}

/* Status pills, re-tuned for the dark ground. */
.certificate .cert-status--valid { background: rgba(255, 255, 255, .92); color: #2f6535; }
.certificate .cert-status--expired { background: rgba(255, 255, 255, .22); color: #fff; }
.certificate .cert-status--revoked { background: #a3261f; color: #fff; }

.certificate .notice { background: rgba(26, 24, 19, .28); border-left-color: #f0b46a; color: #ffe6c4; }
.certificate .cap { color: var(--muted); }

@media (prefers-reduced-motion: reduce) {
  .certificate .cert-sheet::before { animation: none; opacity: .55; }
}

/* ---- The certificate as a standalone artefact

   `is-bare` removes the header and footer entirely (see the layout), so the
   page needs its own top spacing and its own way out. */
/* app.css carries `main > section:first-child:not(.hero) { padding-top: 118px }`
   - room for the "recommended by" ribbon that hangs off the parent site's
   header. That selector is (0,2,2) and out-specifies a plain `body.is-bare main
   > section`, so the bare page kept the full 118px of clearance for a header it
   does not have. Matching :first-child takes it to (0,2,3). */
body.is-bare main > section,
body.is-bare main > section:first-child { padding-top: 28px; }
body.is-bare { background: var(--paper-2); }

@media (max-width: 700px) {
  body.is-bare main > section,
  body.is-bare main > section:first-child { padding-top: 20px; }
}

/* Above the card, in normal flow - not floated over its corner, where it
   overlapped the sheet and read as part of the credential. */
.cert-close {
  display: flex; margin: 0 0 18px auto;
  width: 44px; height: 44px; border-radius: 999px;
  align-items: center; justify-content: center;
  background: var(--white); border: 1px solid var(--line); color: var(--ink-soft);
  box-shadow: 0 3px 12px rgba(26, 24, 19, .1);
  transition: color .15s ease, border-color .15s ease;
}
.cert-close:hover { color: var(--accent); border-color: var(--accent); }

/* ---- The licence, as a row on the account page */
.licence-row {
  display: flex; align-items: center; gap: 20px; flex-wrap: wrap;
  margin-top: 30px; padding: 22px 24px;
  background: var(--white); border: 1px solid var(--line); border-radius: var(--r-lg);
}
.licence-row-main { flex: 1; min-width: 200px; }
.licence-row-label {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted);
}
.licence-row-number { font-size: 21px; margin: 6px 0 8px; letter-spacing: .04em; }
.licence-row-meta {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  font-size: 14.5px; color: var(--muted);
}
.licence-row-cta { flex: none; }

/* A secondary line under a certificate value - "14 Aug 2028 / 1094 days left" */
.cert-sub {
  display: block; margin-top: 3px;
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .06em;
}
.certificate .cert-sub { color: rgba(255, 255, 255, .62); }
