/* Crack the Qase Quests — shared styles.

   BRANDING: every colour below comes from the Qase brand guide, and the dark
   treatment follows the crack-the-qase.io landing page. The --qase-* block is
   the guide transcribed literally — name and hex, nothing derived — plus the
   two values the landing page uses that the guide does not name, marked as
   such. Everything after it is a *role* token built from those: either a plain
   var(), or a color-mix() over them. No rule paints with a literal.

   ONE COMMITTED LOOK. The site is dark, full stop — not light with a dark
   variant, and not a fork on prefers-color-scheme. The landing page it sits
   behind has no light mode either, and a second theme nobody looks at is a
   second theme nobody maintains. `test/branding.test.mjs` fails if a
   prefers-color-scheme fork comes back. */

:root {
  color-scheme: dark;

  /* ── Qase brand guide ─────────────────────────────────────────────────── */
  /* Primary */
  --qase-purple:        #4f46dc;
  --qase-violet:        #c8b0ff;   /* the accent at dark-section weight */
  --qase-brand-black:   #0f172a;   /* the ground */
  /* Secondary — background colours */
  --qase-indigo:        #6c5ce7;
  --qase-lavender:      #9a94ff;
  --qase-periwinkle:    #c2bdff;
  --qase-lavender-dark: #aca1f3;
  --qase-blue:          #b5d2ff;
  --qase-dark-purple:   #1a1636;   /* hero / dark sections */
  /* Neutrals */
  --qase-white:         #ffffff;
  --qase-gray:          #f4f4f4;
  --qase-off-white:     #f3f3f3;
  --qase-lavender-bg:   #f3f2ff;   /* highlighted areas */
  --qase-gray-divider:  #e5e5e5;   /* borders / rules */
  /* Text */
  --qase-foreground:    #171717;
  --qase-muted:         #535353;

  /* ── From the landing page, NOT the brand guide ───────────────────────── */
  /* crack-the-qase.io sets its type in a warm off-white rather than the
     guide's cold #F3F3F3, and that one substitution is most of why the page
     reads as designed instead of as an inverted light theme. Borrowed
     deliberately; flagged so nobody mistakes it for a guide colour. */
  --qase-paper:         #f6f4eb;

  /* ── Roles ────────────────────────────────────────────────────────────── */
  --bg: var(--qase-brand-black);
  /* Panels are a translucent lift, not a second opaque slab, so the ground's
     glow reads through them. Two near-identical dark hues stacked flat was
     what made the first attempt muddy. */
  --panel: color-mix(in srgb, var(--qase-paper) 5%, transparent);
  --ink: var(--qase-paper);
  --ink-soft: color-mix(in srgb, var(--qase-paper) 62%, transparent);
  --line: color-mix(in srgb, var(--qase-paper) 14%, transparent);  /* the landing page's own hairline */
  --accent: var(--qase-violet);
  --accent-ink: var(--qase-brand-black);
  --highlight: color-mix(in srgb, var(--qase-violet) 10%, transparent);
  --code-bg: color-mix(in srgb, var(--qase-brand-black) 70%, #000);
  --code-ink: var(--qase-lavender-bg);
  --field: color-mix(in srgb, var(--qase-brand-black) 55%, #000);

  /* Status colours are NOT in the brand guide — the guide has no semantic
     pass/fail pair. Pitched to sit on the dark ground without shouting. */
  --ok: #5ad6a8;
  --bad: #ff8a7a;
  --warn: #f0c26a;

  --shadow: 0 1px 2px rgba(0, 0, 0, .35), 0 14px 40px rgba(0, 0, 0, .35);
  --radius: 12px;
  --pill: 999px;

  /* Brand type. The faces themselves are not vendored (the site ships no
     remote origins and no binary font payload), so each stack names the brand
     face first and falls back to the platform's nearest equivalent. */
  --display: "General Sans", "General Sans Variable", Inter, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --sans: Inter, "Inter Variable", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: "Geist Mono", "GeistMono Nerd Font", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

/* The guide states this outright: white on brand purple, in either theme. */
::selection { background: var(--qase-purple); color: var(--qase-white); }

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--sans);
  color: var(--ink);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  /* The landing page's ground: brand black with two soft radials, violet at
     top-left and blue at top-right. Fixed, so a long quest page keeps the
     glow where the hero puts it instead of scrolling out from under it. */
  background-color: var(--bg);
  background-image:
    radial-gradient(circle at 10% 20%, color-mix(in srgb, var(--qase-violet) 12%, transparent), transparent 448px),
    radial-gradient(circle at 90% 10%, color-mix(in srgb, var(--qase-blue) 8%, transparent), transparent 384px);
  background-attachment: fixed;
  background-repeat: no-repeat;
  min-height: 100vh;
}

.wrap { max-width: 820px; margin: 0 auto; padding: 2.5rem 1.25rem 4rem; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

.eyebrow {
  font-family: var(--mono);
  font-size: .74rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0 0 .4rem;
}

/* Headings are General Sans; h1 carries the guide's display tracking. */
h1, h2, h3, h4, h5, h6 { font-family: var(--display); font-weight: 600; }
h1 { font-size: 2.35rem; line-height: 1; letter-spacing: -.03em; margin: 0 0 .75rem; }
h2 { font-size: 1.25rem; margin: 2rem 0 .75rem; letter-spacing: -.01em; }
/* The landing page's signature move: the brand word set in violet inside an
   otherwise cream heading. Kept as a span rather than a ::first-word trick so
   the heading still reads as one string to a screen reader and to a test. */
h1 .brand { color: var(--accent); }
.lede { color: var(--ink-soft); font-size: 1.05rem; margin: 0 0 1.5rem; }

.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 1.5rem;
}
.card h2:first-child { margin-top: 0; }

/* The booth offer on the hub. Unlike the per-quest claim this is visible to
   everyone on arrival — it is the reason to start, not the reward for
   finishing — so it takes the guide's highlighted-area treatment rather than
   hiding inside the flag box. */
.callout {
  margin: 0 0 1.5rem;
  padding: .9rem 1.1rem;
  background: var(--highlight);
  border-left: 3px solid var(--accent);
  border-radius: 8px;
  color: var(--ink);
  font-size: .95rem;
}

/* Quest list on the hub */
.quests { list-style: none; margin: 0; padding: 0; display: grid; gap: .75rem; }
.quest {
  display: flex; align-items: baseline; gap: .9rem;
  padding: 1rem 1.15rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: inherit; text-decoration: none;
  transition: border-color .12s ease, box-shadow .12s ease, background-color .12s ease;
}
.quest:hover { text-decoration: none; border-color: var(--accent); background: var(--highlight); box-shadow: var(--shadow); }
.quest:hover .title { color: var(--accent); }
.quest:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.quest .num { font-family: var(--mono); color: var(--ink-soft); font-size: .9rem; min-width: 1.6rem; }
.quest .body { flex: 1; }
.quest .title { font-family: var(--display); font-weight: 600; }
.quest .desc { color: var(--ink-soft); font-size: .92rem; }
.quest[aria-disabled="true"] { opacity: .62; }
.quest[aria-disabled="true"] .title::after { content: ""; }

.badge {
  font-family: var(--mono); font-size: .68rem; letter-spacing: .1em; text-transform: uppercase;
  padding: .2rem .5rem; border-radius: 999px; border: 1px solid var(--line); color: var(--ink-soft);
  white-space: nowrap;
}
.badge.live { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 40%, var(--line)); }

/* Forms */
label { display: block; font-weight: 600; font-size: .9rem; margin: 0 0 .3rem; }
input[type="text"], input[type="password"], textarea {
  width: 100%; padding: .6rem .7rem; font: inherit;
  color: var(--ink); background: var(--field);
  border: 1px solid var(--line); border-radius: 10px;
}
input::placeholder, textarea::placeholder { color: color-mix(in srgb, var(--qase-paper) 38%, transparent); }
input:focus, textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.field { margin: 0 0 1rem; }

/* Password-looking field that isn't one. A real type="password" makes browsers
   and manager extensions offer to save what the player types — unwanted on a
   puzzle page — so those inputs are type="text" masked here instead.
   -webkit-text-security is unprefixed nowhere but supported by Chrome, Edge,
   Safari and Firefox 118+; older Firefox just shows the characters, which is
   harmless when the issued password is printed on the page anyway. */
.masked { -webkit-text-security: disc; text-security: disc; }

/* Show/hide toggle sitting inside a masked field. The field reserves room on
   the right so long input never slides under the button. */
.field.with-reveal { position: relative; }
.field.with-reveal input { padding-right: 4.2rem; }
.reveal {
  position: absolute; right: .35rem; bottom: .35rem;
  padding: .3rem .5rem;
  font-family: var(--mono); font-size: .68rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-soft); background: transparent; border: 1px solid var(--line);
  border-radius: var(--pill);
}
.reveal:hover { color: var(--ink); filter: none; }
.reveal[aria-pressed="true"] { color: var(--accent); border-color: var(--accent); }

/* Pill buttons, as on the landing page: violet on brand black for the primary
   action, a bare cream hairline for the secondary. */
button {
  font: inherit; font-weight: 600; cursor: pointer;
  padding: .55rem 1.15rem; border-radius: var(--pill);
  color: var(--accent-ink); background: var(--accent); border: 1px solid transparent;
}
button:hover { filter: brightness(1.08); }
button.secondary { background: transparent; color: var(--ink); border-color: var(--line); }
button.secondary:hover { background: var(--highlight); border-color: var(--accent); color: var(--ink); filter: none; }

/* Code + query echo */
pre, code { font-family: var(--mono); }
pre {
  background: var(--code-bg); color: var(--code-ink);
  padding: .9rem 1rem; border-radius: 8px; overflow-x: auto; font-size: .86rem;
}
code.inline { background: var(--highlight); color: var(--accent); padding: .1rem .35rem; border-radius: 5px; }

/* Status messages */
.note { padding: .8rem 1rem; border-radius: 8px; border: 1px solid var(--line); background: var(--highlight); margin: 1rem 0; }
.note.ok  { color: var(--ok);  border-color: color-mix(in srgb, var(--ok) 40%, var(--line));  background: color-mix(in srgb, var(--ok) 7%, var(--panel)); }
.note.bad { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 40%, var(--line)); background: color-mix(in srgb, var(--bad) 7%, var(--panel)); }
.note.warn{ color: var(--warn);border-color: color-mix(in srgb, var(--warn) 40%, var(--line));background: color-mix(in srgb, var(--warn) 7%, var(--panel)); }
.hidden { display: none !important; }

/* Flag reveal — the payoff, so it gets the brand accent rather than a status
   colour: lavender BG behind, brand purple on the value. */
.flag {
  margin-top: 1rem; padding: 1.1rem 1.25rem;
  border: 1px dashed var(--accent);
  border-radius: var(--radius);
  background: var(--highlight);
}
.flag .label { font-family: var(--mono); font-size: .72rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-soft); }
.flag .value { font-family: var(--mono); font-size: 1.15rem; font-weight: 700; color: var(--accent); user-select: all; overflow-wrap: anywhere; }
/* The booth call-to-action. It lives INSIDE .flag so every quest's existing
   reveal carries it for free — no quest needed a wiring change for it. Body
   face, not mono: it is a sentence to read, not a value to copy. */
.flag .claim {
  margin: .75rem 0 0;
  padding-top: .7rem;
  border-top: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  font-size: .92rem;
  color: var(--ink-soft);
}

.backlink { display: inline-block; margin-bottom: 1.5rem; font-family: var(--mono); font-size: .85rem; }
footer { margin-top: 3rem; color: var(--ink-soft); font-size: .82rem; }
