/* The player-facing account pages.
   ---------------------------------------------------------------------------------------------------
   Dressed as Capverse rather than as an internal tool. This is the one surface a player sees while
   locked out and anxious about their account, and a page that looks nothing like the game it belongs
   to reads as a phishing site -- which is exactly the instinct we want players to keep everywhere else.

   The whole scene is CSS -- layered radial gradients on two fixed layers -- so the only image on the
   page is the logo. That is not decoration-for-free: this page is
   opened on a phone, often on mobile data, by someone who cannot get into their account, and every
   background image would be weight between them and the form.

   The CSP for this page is `style-src 'self'` with no 'unsafe-inline', so EVERYTHING lives here --
   there are no style attributes and no <style> blocks in the markup, and adding one would silently do
   nothing. */

:root {
  color-scheme: dark;

  /* Taken off the 2.0 key art: deep indigo-violet space, warm logo highlights, cyan for links. */
  --space-0:#0a0219;
  --space-1:#150733;
  --space-2:#231052;
  --violet:#7b3fe4;
  --magenta:#e0559b;
  --orange:#ff9040;
  --cyan:#4fd6ff;

  --fg:#f2ecff;
  --mut:#a08fca;
  --line:rgba(160,120,255,.22);
  --glass:rgba(28,12,58,.62);
  --danger:#ff6b7f;
  --ok:#4fe0a8;
}

* { box-sizing:border-box; }
[hidden] { display:none !important; }

/* The base colour goes on `html` as well, so any region the fixed layers below have not composited
   yet falls through to deep space rather than to the canvas default, which is BLACK. */
html { background:#06010f; }
html, body { min-height:100%; }

body {
  margin:0;
  color:var(--fg);
  /* TRADITIONAL Chinese fonts come BEFORE Simplified ones, because the site defaults to Traditional and
     the glyphs genuinely differ -- 說 vs 说, 電 vs 电. A Simplified-first stack renders Traditional text
     in a face designed for the other script, which a native reader notices immediately even when every
     character is technically correct. The previous stack named only SC faces. Japanese is listed after
     both, so kanji shared with Chinese still prefer a CJK face over a Latin fallback. */
  font:16px/1.55 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,
       "PingFang TC","Noto Sans TC","Noto Sans CJK TC","Microsoft JhengHei",
       "PingFang SC","Noto Sans SC","Noto Sans CJK SC","Microsoft YaHei",
       "Hiragino Sans","Noto Sans JP",sans-serif;
  /* 16px base, never smaller on inputs: iOS zooms the viewport on focus for anything under 16px, and a
     page that lurches sideways mid-typing is miserable on the device most of these players are on. */
  background:var(--space-0);
}

/* The scene is painted on TWO FIXED LAYERS rather than on the body with background-attachment:fixed.
   That property is viewport-sized but scroll-repainted, and on a long page browsers routinely fail to
   repaint it in time -- which showed up here as a hard flat band tearing across the artwork mid-scroll,
   and is worse on iOS Safari. A position:fixed element is composited once and simply does not do that.

   Both layers are pointer-events:none, so nothing decorative can ever swallow a tap on the form. */
body::before,
body::after {
  content:""; position:fixed; inset:0; pointer-events:none; z-index:0;
}

/* nebula clouds, warm on the left and cool on the right, mirroring the key art */
body::before {
  background:
    radial-gradient(60% 45% at 18% 12%, rgba(224,85,155,.28), transparent 70%),
    radial-gradient(55% 40% at 82% 8%,  rgba(79,214,255,.16), transparent 72%),
    radial-gradient(70% 50% at 50% 55%, rgba(123,63,228,.30), transparent 75%),
    radial-gradient(45% 35% at 12% 78%, rgba(255,144,64,.12), transparent 70%),
    linear-gradient(180deg, var(--space-1) 0%, var(--space-0) 60%, #06010f 100%);
}

/* Starfield, one layer over the nebula. Two dot scales in a single background so it reads as depth
   without needing a third stacking context. */
body::after {
  background-image:
    radial-gradient(1.4px 1.4px at 20% 30%, rgba(255,255,255,.75), transparent),
    radial-gradient(1.2px 1.2px at 70% 15%, rgba(255,255,255,.55), transparent),
    radial-gradient(1.6px 1.6px at 45% 70%, rgba(255,255,255,.65), transparent),
    radial-gradient(1.1px 1.1px at 88% 55%, rgba(255,255,255,.45), transparent),
    radial-gradient(1.3px 1.3px at 12% 85%, rgba(255,255,255,.50), transparent),
    radial-gradient(1px 1px at 60% 40%, rgba(190,220,255,.55), transparent),
    radial-gradient(1px 1px at 25% 60%, rgba(255,215,235,.45), transparent);
  background-size:340px 340px, 340px 340px, 340px 340px, 340px 340px, 340px 340px, 180px 180px, 180px 180px;
  opacity:.5;
}

/* --- layout ------------------------------------------------------------------------------------ */
/* A sticky footer, so on the short states -- "your password is changed", "request received" -- the
   social links sit at the bottom of the window instead of floating halfway up an empty page. */
body { display:flex; flex-direction:column; min-height:100vh; }

.shell {
  position:relative; z-index:1;
  flex:1;
  width:100%; max-width:460px; margin:0 auto; padding:36px 20px 24px;
}

.pitch { text-align:center; }

/* --- brand ------------------------------------------------------------------------------------- */
.brand { display:block; margin:0 0 22px; }
/* The Cap Infra mark is a near-square disc, where the old wordmark was a wide lockup. Carrying the
   230px width over would have printed a disc half the width of the card. Sized to read as a mark. */
.brand img {
  width:100%; max-width:132px; height:auto;
  filter:drop-shadow(0 10px 26px rgba(123,63,228,.55));
}

/* Capverse 2.0 at the foot: the product this account belongs to, not the operator. Deliberately
   quiet -- dimmed until hovered -- so it reads as context and never competes with the mark at the
   top, which is the one someone checks before typing a password. */
.foot-game {
  display:block; width:100%; max-width:104px; height:auto;
  margin:0 auto 10px; opacity:.55; transition:opacity .15s ease;
}
.foot-game:hover { opacity:.85; }

/* --- the card ---------------------------------------------------------------------------------- */
/* One panel holding the whole task. Glassy rather than flat so it lifts off the nebula without
   needing a hard edge, which would fight the artwork. */
.card {
  background:var(--glass);
  border:1px solid var(--line);
  border-radius:18px;
  padding:26px 22px 28px;
  box-shadow:0 22px 60px rgba(4,0,20,.55), inset 0 1px 0 rgba(255,255,255,.06);
  backdrop-filter:blur(14px);
  -webkit-backdrop-filter:blur(14px);
}

h1 {
  font-size:23px; line-height:1.25; margin:0 0 10px; letter-spacing:.2px;
  /* A gradient clipped to text: the fallback matters, because if background-clip is unsupported the
     text would otherwise be transparent and the page would have no heading at all. */
  color:var(--fg);
  background:linear-gradient(92deg, #fff 0%, #ffd9b0 45%, var(--magenta) 100%);
}
@supports ((-webkit-background-clip:text) or (background-clip:text)) {
  h1 { -webkit-background-clip:text; background-clip:text; color:transparent; }
}
h2 { font-size:17px; margin:0 0 8px; }
p.sub { color:var(--mut); font-size:14px; margin:0 0 22px; }

/* --- form -------------------------------------------------------------------------------------- */
form { display:flex; flex-direction:column; }
label { font-size:12.5px; letter-spacing:.3px; text-transform:uppercase; color:var(--mut); margin:18px 0 7px; }

input, select {
  font:inherit; color:var(--fg);
  background:rgba(10,2,25,.55);
  border:1px solid var(--line); border-radius:12px;
  padding:13px 14px; width:100%;
  transition:border-color .15s, box-shadow .15s, background .15s;
}
input::placeholder { color:#7a6aa0; }
input:hover, select:hover { border-color:rgba(160,120,255,.42); }
input:focus, select:focus {
  outline:none;
  border-color:var(--violet);
  background:rgba(14,4,34,.75);
  box-shadow:0 0 0 3px rgba(123,63,228,.30);
}
/* The select keeps a chevron of its own, drawn as a data: URI. Stripping the native appearance without
   putting one back leaves a control that gives no sign it opens -- and this one carries the choice the
   whole rest of the form depends on. (img-src allows data:, so this is CSP-clean.) */
select {
  appearance:none; -webkit-appearance:none; cursor:pointer;
  padding-right:40px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%23a08fca' d='M1 1.5 6 6.5l5-5'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:right 15px center;
  background-size:12px 8px;
}
select option { background:#1a0a36; color:var(--fg); }

/* File pickers. Left native, the button is a grey OS chip sitting on the artwork -- the single most
   out-of-place element on the page, and it appears on the ID route, which is the one a worried player
   is most likely to be using. */
input[type=file] { padding:9px 12px; cursor:pointer; }
input[type=file]::file-selector-button {
  font:inherit; font-size:14px; cursor:pointer;
  margin-right:12px; padding:8px 14px;
  color:var(--fg); background:rgba(255,255,255,.08);
  border:1px solid var(--line); border-radius:9px;
  transition:background .15s, border-color .15s;
}
input[type=file]::file-selector-button:hover {
  background:rgba(255,255,255,.14); border-color:rgba(160,120,255,.45);
}

button {
  font:inherit; cursor:pointer; color:var(--fg);
  background:rgba(255,255,255,.06);
  border:1px solid var(--line); border-radius:12px; padding:12px 16px;
  transition:background .15s, border-color .15s, transform .08s;
}
button:hover:not(:disabled) { background:rgba(255,255,255,.11); border-color:rgba(160,120,255,.45); }
button:active:not(:disabled) { transform:translateY(1px); }

/* The one action that finishes the task, so it is the one thing on the page wearing the logo's warm
   gradient. Everything else is deliberately quieter. */
button.primary {
  margin-top:26px; font-weight:700; letter-spacing:.2px; color:#2a0a12; border:none;
  background:linear-gradient(96deg, var(--orange) 0%, var(--magenta) 100%);
  box-shadow:0 10px 26px rgba(224,85,155,.35);
}
button.primary:hover:not(:disabled) {
  box-shadow:0 12px 32px rgba(224,85,155,.5);
  background:linear-gradient(96deg, #ffa055 0%, #ea66a8 100%);
}
button:disabled { opacity:.45; cursor:not-allowed; box-shadow:none; }

.row { display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin-top:14px; }

/* An input and its button as one control. The input takes the slack; the button is sized by its own
   text and never shrinks below it, so the code box is what gives up width on a narrow screen -- which
   is the right way round, since it holds six characters and the button holds a sentence. */
.pair { display:flex; align-items:stretch; gap:10px; }
.pair input { flex:1 1 auto; min-width:0; }
.pair button { flex:0 0 auto; white-space:nowrap; }

/* A label with a control sitting opposite it. baseline, not center, so the small text and the label
   sit on the same line rather than looking nudged. */
.label-row { display:flex; align-items:baseline; justify-content:space-between; gap:12px; }
.label-row label { margin-bottom:7px; }

/* A button that reads as a link: this is a view control, not an action, and giving it a button's
   weight would put it in competition with the one that submits the form. */
button.linkish {
  border:none; background:none; padding:0; margin:0;
  color:var(--cyan); font-size:12.5px; letter-spacing:.3px; text-transform:uppercase;
  cursor:pointer;
}
button.linkish:hover:not(:disabled) { background:none; text-decoration:underline; }
button.linkish:focus-visible { outline:2px solid var(--violet); outline-offset:3px; border-radius:4px; }

/* --- the route chooser -------------------------------------------------------------------------- */
/* Real buttons, not a dropdown. A select asks someone to commit before they can see what any option
   costs them, and the two heavy options here -- photographing an ID, connecting a wallet -- are
   exactly the ones people want to weigh first. Each card says what it needs before it is chosen. */
.choose-h { font-size:16px; margin:0 0 16px; color:var(--fg); }

.choice {
  display:block; width:100%; text-align:left;
  margin:0 0 12px; padding:16px 18px;
  border:1px solid var(--line); border-radius:14px;
  background:rgba(10,2,25,.45);
}
.choice:hover:not(:disabled) {
  background:rgba(46,20,92,.55); border-color:rgba(160,120,255,.5);
}
.choice:focus-visible { outline:2px solid var(--violet); outline-offset:2px; }
.choice-t { display:block; font-weight:600; font-size:15.5px; margin-bottom:5px; }
/* Normal weight and muted: this is the part someone reads to decide, so it must not compete with the
   name of the option it belongs to. */
.choice-d { display:block; color:var(--mut); font-size:13.5px; line-height:1.5; font-weight:400; }

/* Back out of a chosen route. Sits above the heading, where a back control is looked for. */
/* Sits UNDER the submit button now, so the spacing moved from below it to above it. Full width and
   centred rather than a small left-aligned link, so it reads as the deliberate secondary action
   beneath the primary one instead of a stray fragment hanging off the corner. */
button.linkish.back { display:block; width:100%; margin:14px 0 0; text-align:center; text-transform:none; font-size:13px; }
.route-desc { margin:0 0 4px; }
.note { color:var(--ok); font-size:13px; margin:9px 0 0; }
.hint { color:var(--mut); font-size:13px; margin:7px 0 0; }

/* The worked example under an upload field or the wallet button.
 *
 * <details> rather than a scripted panel: it opens with no JavaScript, it is keyboard operable and
 * announced as a disclosure for free, and this page has to keep working for someone who has already
 * lost access to their account once today. */
.guide { margin:8px 0 0; }
.guide > summary {
  display:inline-flex; align-items:center; gap:6px;
  width:auto; cursor:pointer; list-style:none;
  color:var(--cyan); font-size:13px; padding:2px 0;
}
.guide > summary::-webkit-details-marker { display:none; }
/* Own triangle, so the affordance is identical across browsers and rotates on open. */
.guide > summary::before {
  content:""; width:0; height:0;
  border-left:5px solid currentColor;
  border-top:4px solid transparent; border-bottom:4px solid transparent;
  transition:transform .15s ease;
}
.guide[open] > summary::before { transform:rotate(90deg); }
.guide > summary:hover { color:var(--fg); }
.guide > summary:focus-visible { outline:2px solid var(--cyan); outline-offset:3px; border-radius:4px; }

/* The instructions are baked into the pixels, so they cannot reflow: on a narrow phone the type ends
 * up far too small to read in place. The image is therefore a link to itself, which hands the reader
 * their browser's own full-screen viewer and its pinch-zoom. Cheaper and more reliable than a
 * lightbox, and it survives the script not running. */
.guide-shot { display:block; margin:8px 0 2px; }
.guide-shot img {
  display:block; width:100%; height:auto;
  border:1px solid var(--line); border-radius:12px;
  background:var(--space-0);
}
.guide-shot:focus-visible { outline:2px solid var(--cyan); outline-offset:3px; border-radius:14px; }

/* One message element for every outcome, coloured by class. A single place to look means an error can
   never be rendered somewhere the player has already scrolled past. */
.msg {
  margin:20px 0 0; padding:12px 14px; border-radius:12px;
  border:1px solid var(--line); background:rgba(10,2,25,.5); font-size:14px;
}
.msg.err { border-color:rgba(255,107,127,.5); color:var(--danger); background:rgba(70,10,25,.35); }
.msg.ok  { border-color:rgba(79,224,168,.5); color:var(--ok);     background:rgba(6,45,32,.35); }

.done {
  margin-top:22px; padding:22px;
  background:linear-gradient(180deg, rgba(79,224,168,.10), rgba(10,2,25,.35));
  border:1px solid rgba(79,224,168,.45); border-radius:14px;
}
/* Was margin:0, which ran every line of the panel together into one grey block. The panel now
   carries five separate statements and the player has to be able to tell them apart -- especially
   the case number, which sits alone between them. */
.done p { margin:0 0 10px; color:var(--mut); font-size:14px; }
.done p:last-child { margin-bottom:0; }
.case { font-size:20px; font-weight:700; letter-spacing:1px; color:var(--cyan); }

.foot { margin-top:22px; color:var(--mut); font-size:14px; text-align:center; }
a { color:var(--cyan); text-decoration:none; }
a:hover { text-decoration:underline; }

#challenge { margin-top:22px; min-height:0; }
#challenge:not(:empty) { min-height:70px; }

/* --- site footer ------------------------------------------------------------------------------- */
/* Outside the card on purpose. These are the official channels, and a player who arrives unsure
   whether this page is really Capverse can leave through a door they already recognise -- which is a
   real anti-phishing affordance, not decoration. */
/* position/z-index are NOT cosmetic here. The nebula layer is a fixed pseudo-element at z-index:0, and
   a static element paints BELOW a positioned one -- so without this the whole footer renders behind
   the artwork and is invisible, while still being present in the DOM and reachable by a screen reader.
   It shipped that way once; `main` had the z-index and this did not. */
/* --- language selector ---------------------------------------------------------------------------
   Top-right, above the card rather than buried in the footer: someone who cannot read the page needs
   to find this before they read anything, and the top corner is where every site has trained them to
   look.

   position/z-index are NOT optional. The nebula and starfield are fixed layers at z-index 0, and a
   static element lands underneath them -- which is exactly how the footer once shipped invisible. */
.lang-bar {
  position:relative; z-index:1;
  display:flex; justify-content:flex-end;
  padding:14px 20px 0;
  /* width:100% is load-bearing. `body` is a column flexbox, so without it this is a flex ITEM that
     shrinks to its content, and `margin:0 auto` then centres a 194px strip in the middle of the page
     instead of parking the selector in the top-right corner. Caught by measuring, not by looking. */
  width:100%; max-width:1120px; margin:0 auto; box-sizing:border-box;
}

.lang-select {
  appearance:none;
  /* The form controls in the card are `width:100%` by design (a full-width field is easier to hit on a
     phone), and a bare `select` inherits that rule. Here it stretched the language picker across the
     whole page. Opted out explicitly rather than by narrowing the global rule, which would change every
     field on both pages to fix one control. */
  width:auto;
  background:var(--glass);
  color:var(--fg);
  border:1px solid var(--line);
  border-radius:9px;
  padding:7px 30px 7px 12px;
  font:inherit; font-size:13.5px; line-height:1.2;
  cursor:pointer;
  /* The chevron is a background image rather than a pseudo-element, because a <select> cannot host
     children. Inlined as a data URI: img-src allows data:, and this avoids a request for 200 bytes. */
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%23a08fca' d='M1 1.5 6 6.5l5-5'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:right 11px center;
  background-size:11px;
}
.lang-select:hover { border-color:rgba(160,120,255,.42); }
.lang-select:focus-visible { outline:2px solid var(--cyan); outline-offset:2px; }
/* The dropdown list itself is drawn by the OS and does NOT inherit the page's colours, so the options
   need their own dark background or they render as black-on-white on most desktop browsers. */
.lang-select option { background:var(--space-1); color:var(--fg); }

@media (max-width:600px) {
  .lang-bar { padding:10px 14px 0; }
}

.site-foot {
  position:relative; z-index:1;
  margin:0 auto; width:100%; max-width:820px; padding:8px 20px 36px; text-align:center;
}

.socials { display:flex; justify-content:center; gap:12px; flex-wrap:wrap; margin:0 0 16px; padding:0; list-style:none; }
.socials a {
  display:inline-flex; align-items:center; gap:8px;
  padding:9px 14px; border-radius:999px;
  border:1px solid var(--line); background:rgba(28,12,58,.5);
  color:var(--fg); font-size:13.5px; text-decoration:none;
  transition:border-color .15s, background .15s, transform .1s;
}
.socials a:hover {
  border-color:rgba(160,120,255,.55); background:rgba(46,20,92,.7);
  transform:translateY(-1px); text-decoration:none;
}
.socials svg { width:16px; height:16px; flex:none; fill:currentColor; }

.legal { color:#7a6aa0; font-size:12.5px; margin:0; }

/* --- responsive -------------------------------------------------------------------------------- */

/* Small phones. The card loses its inset so the form gets the width back; on a 320px screen the
   difference between 22px and 16px of padding is a visible amount of input. */
@media (max-width:400px) {
  .shell { padding:26px 14px 20px; }
  .card { padding:22px 16px 24px; border-radius:15px; }
  .pair { gap:8px; }
  .pair button { padding:12px 10px; font-size:14px; }
  .socials a { padding:8px 12px; font-size:13px; }
}

/* Landscape phones and short windows. Vertical space is the scarce thing here, not horizontal, so the
   logo shrinks rather than the type. */
@media (max-height:560px) and (min-width:480px) {
  .shell { padding-top:20px; }
  .brand { margin-bottom:14px; }
  .brand img { max-width:150px; }
}

/* TWO COLUMNS from 880px. Below this a side-by-side layout would squeeze the form narrower than a
   phone gives it, which is the wrong trade -- the form is the point of the page.

   Above it, the previous design left a 460px strip stranded in the middle of a desktop window. The
   left column takes the brand and the framing, the right takes the task, and the source order is
   unchanged so a screen reader and a phone still read it top to bottom. */
@media (min-width:880px) {
  .shell {
    display:grid;
    grid-template-columns:minmax(0,1fr) minmax(0,480px);
    align-items:start;
    gap:56px;
    max-width:1000px;
    padding:64px 40px 40px;
  }

  .pitch { text-align:left; padding-top:8px; }
  .brand { margin-bottom:26px; }
  .brand img { max-width:300px; }

  /* Bigger, because on a desktop this column is the page's voice rather than a caption above a form. */
  h1 { font-size:34px; margin-bottom:14px; }
  p.sub { font-size:15.5px; max-width:42ch; margin-bottom:0; }

  .card { padding:30px 28px 32px; }

  /* The left column follows the reader down a long form (the recovery page is tall) instead of
     scrolling away and leaving the form floating next to nothing. */
  .pitch { position:sticky; top:40px; }
}

/* Wide desktops. Nothing grows past this: a 90-character line of help text is harder to read than a
   60-character one, and an input box the width of a monitor looks broken rather than generous. */
@media (min-width:1280px) {
  .shell { gap:80px; }
}

/* The scene itself does not move, so there is nothing to disable there. What remains are the small
   press and hover shifts, which are exactly what someone asking for less motion is asking to lose. */
@media (prefers-reduced-motion:reduce) {
  button:active:not(:disabled), .socials a:hover { transform:none; }
  /* The guide disclosure's triangle rotates as it opens. Still shows its state, just without the
     spin: the transition goes, the rotation stays. */
  .guide > summary::before, .foot-game { transition:none; }
}
