/* Palette aligned to moa.place (assets/css/style.css): warm cream ground, warm
   ink, and the moa brick-red #b5402f as the single brand and primary colour.
   The brand red is identical in both themes; only the neutrals flip. */
:root {
  --ground: #faf8f4;
  --panel: #ffffff;
  --ink: #1c1b19;
  --ink-soft: #8b867d;
  --ink-faint: #b3ada2;
  --line: #e4e0d8;
  --field: #ffffff;
  --field-line: #ddd8cc;
  --moa: #b5402f;
  --moa-deep: #93321f;
  --primary: var(--moa);
  --primary-press: var(--moa-deep);
  --on-primary: #faf8f4;
  --accent: var(--moa);
  /* Brand red as small text needs lifting on the dark ground; light keeps #b5402f. */
  --accent-text: var(--moa);
  --ring: color-mix(in srgb, #b5402f 28%, transparent);
  --error: #a5342a;
  --error-bg: color-mix(in srgb, #a5342a 11%, transparent);
  --info-bg: color-mix(in srgb, #1c1b19 6%, transparent);
  --brand-bg: #201f1d;
  --brand-fg: #f2f1ee;
  --shadow: 0 1px 2px rgba(28,27,25,.05), 0 14px 34px -14px rgba(28,27,25,.20);
  --radius-btn: 12px;
  /* Selects draw their own arrow (appearance: none), so the glyph must be a
     data URI per theme — a background-image cannot take currentColor. */
  --chevron: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 8"><path d="M1 1.5 6 6.5 11 1.5" fill="none" stroke="%23b3ada2" stroke-width="1.8" stroke-linecap="round"/></svg>');
  color-scheme: light dark;
}
@media (prefers-color-scheme: dark) {
  :root {
    --ground: #000000;
    --panel: #141414;
    --ink: #f2f1ee;
    --ink-soft: #9a958c;
    --ink-faint: #6f6a62;
    --line: #2a2a2a;
    --field: #1b1b1a;
    --field-line: #343230;
    --accent-text: #d3705b;
    --ring: color-mix(in srgb, #b5402f 45%, transparent);
    --error: #ef8078;
    --error-bg: color-mix(in srgb, #ef8078 14%, transparent);
    --info-bg: color-mix(in srgb, #f2f1ee 7%, transparent);
    --shadow: 0 1px 2px rgba(0,0,0,.4), 0 16px 40px -16px rgba(0,0,0,.7);
    --chevron: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 8"><path d="M1 1.5 6 6.5 11 1.5" fill="none" stroke="%236f6a62" stroke-width="1.8" stroke-linecap="round"/></svg>');
  }
}
:root[data-theme="light"] {
  --ground: #faf8f4; --panel: #ffffff; --ink: #1c1b19; --ink-soft: #8b867d;
  --ink-faint: #b3ada2; --line: #e4e0d8; --field: #ffffff; --field-line: #ddd8cc;
  --accent-text: var(--moa); --ring: color-mix(in srgb, #b5402f 28%, transparent);
  --error: #a5342a; --error-bg: color-mix(in srgb, #a5342a 11%, transparent);
  --info-bg: color-mix(in srgb, #1c1b19 6%, transparent);
  --shadow: 0 1px 2px rgba(28,27,25,.05), 0 14px 34px -14px rgba(28,27,25,.20);
  --chevron: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 8"><path d="M1 1.5 6 6.5 11 1.5" fill="none" stroke="%23b3ada2" stroke-width="1.8" stroke-linecap="round"/></svg>');
}
:root[data-theme="dark"] {
  --ground: #000000; --panel: #141414; --ink: #f2f1ee; --ink-soft: #9a958c;
  --ink-faint: #6f6a62; --line: #2a2a2a; --field: #1b1b1a; --field-line: #343230;
  --accent-text: #d3705b; --ring: color-mix(in srgb, #b5402f 45%, transparent);
  --error: #ef8078; --error-bg: color-mix(in srgb, #ef8078 14%, transparent);
  --info-bg: color-mix(in srgb, #f2f1ee 7%, transparent);
  --shadow: 0 1px 2px rgba(0,0,0,.4), 0 16px 40px -16px rgba(0,0,0,.7);
  --chevron: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 8"><path d="M1 1.5 6 6.5 11 1.5" fill="none" stroke="%236f6a62" stroke-width="1.8" stroke-linecap="round"/></svg>');
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body { height: 100%; }
body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Segoe UI", "Noto Sans KR", sans-serif;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* ---------- shell / responsive split ---------- */
.shell { min-height: 100%; display: flex; }
.brand { display: none; }
/* min-width: 0 because a flex item defaults to min-width: auto and so refuses
   to shrink below the column it holds. On anything narrower than 420px — which
   is most phones — the page was laid out wider than the screen and the right
   edge of every field sat outside it. */
.auth {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem 1.25rem;
}
.col { width: 100%; max-width: 380px; padding-bottom: 3.5rem; }

@media (min-width: 920px) {
  .brand {
    display: flex;
    position: relative;
    flex: 1 1 46%;
    max-width: 620px;
    overflow: hidden;
    color: var(--brand-fg);
    background: var(--brand-bg);
    padding: 3.5rem;
    align-items: flex-end;
  }
  .brand-ghost {
    position: absolute;
    top: -.18em; left: -.06em;
    font-family: "Futura", "Avenir Next", "Century Gothic", "Jost", system-ui, sans-serif;
    font-weight: 700;
    font-size: 30rem;
    line-height: .8;
    letter-spacing: -.02em;
    color: var(--brand-fg);
    opacity: .05;
    pointer-events: none;
    user-select: none;
  }
  .brand-inner { position: relative; z-index: 1; }
  .brand-inner .logo-hero { color: var(--brand-fg); margin-bottom: 1.7rem; }
  .brand-tag {
    font-size: 1.45rem;
    line-height: 1.5;
    font-weight: 500;
    letter-spacing: -.01em;
    margin: 0 0 1.3rem;
    text-wrap: balance;
  }
  .brand-services {
    font-size: .82rem;
    letter-spacing: .18em;
    text-transform: uppercase;
    opacity: .65;
    margin: 0;
  }
  .auth { flex: 1 1 54%; padding: 2rem; }
  .col { max-width: 360px; }
}

/* ---------- brand header (MOA PLACE wordmark logo) ---------- */
.head { text-align: center; margin-bottom: 2.2rem; }
.logo { display: inline-flex; color: var(--ink); }
.logo .wordmark-svg { display: block; width: auto; height: 2.7rem; }
.logo-hero .wordmark-svg { height: 3.7rem; }
/* MOA carries the brand red; PLACE follows the ink, so it reads black on light
   grounds and white on dark ones (including the dark brand panel). */
.wordmark-svg .wm-moa { fill: var(--moa); }
.wordmark-svg .wm-place { fill: currentColor; }
.tagline { margin: 1rem 0 0; color: var(--ink-soft); font-size: .95rem; }

/* ---------- views ---------- */
.view { display: flex; flex-direction: column; gap: .7rem; }
/* A block of its own width: as a bare inline <a> — which is what the sign-up
   page has — the padding and margin below it are dropped from the layout
   entirely and the first field lands against it. */
.back {
  display: block; width: fit-content; align-self: flex-start;
  background: none; border: none; color: var(--ink-soft);
  font-size: .9rem; padding: .2rem 0; margin-bottom: .3rem; cursor: pointer;
}
.back:hover { color: var(--ink); }
.hint { margin: 0 0 .3rem; color: var(--ink-soft); font-size: .92rem; }

/* ---------- buttons ---------- */
.btn {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  width: 100%; min-height: 52px;
  padding: 0 3rem;
  border: 1px solid transparent; border-radius: var(--radius-btn);
  font-size: 1rem; font-weight: 600; font-family: inherit;
  cursor: pointer;
  text-decoration: none;
  transition: transform .06s ease, background-color .15s ease, border-color .15s ease, opacity .15s ease;
}
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .55; cursor: not-allowed; }
.btn:disabled:active { transform: none; }
.btn-primary:disabled:hover { background: var(--primary); }
.btn .ic { position: absolute; left: 18px; width: 21px; height: 21px; }
.btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }

.btn-primary { background: var(--primary); color: var(--on-primary); }
.btn-primary:hover { background: var(--primary-press); }
/* Chooser's email path is the main action, so it carries the brand red fill and
   a soft lift; the providers below keep their own mandated colours. */
.btn-email { background: var(--primary); color: var(--on-primary); box-shadow: var(--shadow); }
.btn-email:hover { background: var(--primary-press); }
.btn-social { background: var(--panel); color: var(--ink); border-color: var(--field-line); }
.btn-social:hover { border-color: var(--ink-faint); }
/* Brand buttons use each provider's mandated colours, fixed across themes. */
.btn-kakao { background: #fee500; color: #191600; }
.btn-kakao:hover { background: #f5dd00; }
.btn-naver { background: #03c75a; color: #fff; }
.btn-naver:hover { background: #02b351; }
/* Google — white button, fixed (per Google branding). Border keeps it visible on
   light grounds; on dark grounds the white fill stands on its own. */
.btn-google { background: #fff; color: #1f1f1f; border-color: #dadce0; }
.btn-google:hover { background: #f7f8f8; }
.btn-google:focus-visible { box-shadow: 0 0 0 3px rgba(0,0,0,.28); }
/* Sign in with Apple — black button, per Apple HIG. The hairline border only
   registers on dark grounds, keeping the black button delineated there. */
.btn-apple { background: #000; color: #fff; border-color: rgba(255,255,255,.18); }
.btn-apple:hover { background: #1a1a1a; }
.btn-apple:focus-visible { box-shadow: 0 0 0 3px rgba(255,255,255,.35), 0 0 0 5px rgba(0,0,0,.4); }
.btn img.ic { object-fit: contain; }
.btn-ghost { background: none; color: var(--ink-soft); min-height: 46px; }
.btn-ghost:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 5%, transparent); }

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

/* ---------- divider ---------- */
.divider {
  display: flex; align-items: center; gap: .8rem;
  color: var(--ink-faint); font-size: .8rem; margin: .3rem 0;
}
.divider::before, .divider::after {
  content: ""; flex: 1; height: 1px; background: var(--line);
}

/* ---------- form fields ---------- */
form { display: flex; flex-direction: column; gap: .8rem; margin: 0; }
.field {
  display: flex; flex-direction: column; gap: .38rem;
  font-size: .82rem; font-weight: 600; color: var(--ink-soft);
}
/* The label text and its "(optional)" ride in one element. As two children of
   the column they became two flex items and broke onto separate lines. The class
   keeps this off .phone-row and .input-wrap, which are spans in the same place
   and want their own layout. */
.field > .lbl { display: block; }
.field em { font-style: normal; font-weight: 500; color: var(--ink-faint); }
.field input {
  min-height: 50px; padding: 0 .9rem;
  border: 1px solid var(--field-line); border-radius: 12px;
  background: var(--field); color: var(--ink);
  font-size: 1rem; font-family: inherit; font-weight: 400;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.field input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--ring); }
.field input::placeholder { color: var(--ink-faint); }
/* Live validation: red as soon as a whole value is known to be wrong. */
.field input.err { border-color: var(--error); }
.field input.err:focus { border-color: var(--error); box-shadow: 0 0 0 3px var(--error-bg); }

/* ---------- captcha ---------- */
/* The answer sits beside the image, filling what used to be dead space; on a
   width where it cannot breathe it wraps to its own full line instead. */
.captcha-row { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; }
.captcha-img {
  flex: none; width: 150px; height: 52px;
  border: 1px solid var(--field-line); border-radius: 10px; background: #f0eee9;
}
.captcha-answer {
  flex: 1 1 6rem; min-width: 6rem; min-height: 50px; padding: 0 .9rem;
  border: 1px solid var(--field-line); border-radius: 12px;
  background: var(--field); color: var(--ink);
  font-size: 1rem; font-family: inherit; font-weight: 500;
  text-transform: uppercase; letter-spacing: .18em;
}
.captcha-answer::placeholder { text-transform: none; letter-spacing: normal; color: var(--ink-faint); }
.captcha-answer:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--ring); }
.icon-btn {
  flex: none; display: grid; place-items: center;
  width: 40px; height: 40px; border-radius: 10px;
  border: 1px solid var(--field-line); background: var(--panel); color: var(--ink-soft);
  cursor: pointer;
}
.icon-btn svg { width: 18px; height: 18px; }
.icon-btn:hover { color: var(--ink); border-color: var(--ink-faint); }
.icon-btn:active { transform: rotate(-45deg); }
.icon-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }

/* ---------- password reveal ---------- */
.input-wrap { position: relative; display: block; }
.input-wrap input { width: 100%; padding-right: 3rem; }

/* Shown on /signup when the visitor already has a session, offering the choice
   between continuing and registering a second account. */
.notice { margin: 0 0 1rem; }
.notice p { margin: 0 0 .9rem; }

/* Dial code beside the national number. The selector is sized to the longest
   endonym in the list without letting it crowd out the number itself. */
.phone-row { display: flex; gap: .5rem; align-items: stretch; }
.phone-row select { flex: 0 1 11rem; min-width: 0; }
.phone-row input { flex: 1 1 auto; min-width: 0; }
/* dial.js wraps the selector and paints the chosen dial code on a chip over
   its closed face; the list underneath keeps the full names. The chip sits
   inside the border and stops short of the selector's own drawn arrow, so
   border, focus ring and arrow stay the selector's, and the .field select
   padding-right ends the hidden name before the strip the chip leaves
   uncovered. 6rem fits the widest code (+971). */
.phone-row .dial-wrap { position: relative; display: flex; flex: 0 0 auto; width: 6rem; }
.dial-code {
  position: absolute; inset: 1px 1.9rem 1px 1px;
  display: flex; align-items: center; padding-left: .9rem;
  background: var(--field); color: var(--ink);
  border-radius: 11px 0 0 11px; pointer-events: none;
}
.reveal, .cal-btn {
  position: absolute; right: .45rem; top: 50%; transform: translateY(-50%);
  display: grid; place-items: center; width: 34px; height: 34px;
  border: none; background: none; color: var(--ink-faint); cursor: pointer; border-radius: 8px;
}
.reveal svg, .cal-btn svg { width: 20px; height: 20px; }
.reveal:hover, .cal-btn:hover { color: var(--ink-soft); }
.reveal .i-eyeoff { display: none; }
.reveal.on .i-eye { display: none; }
.reveal.on .i-eyeoff { display: block; }
.fine { margin: -.1rem 0 .1rem; font-size: .82rem; color: var(--ink-faint); }
/* The line that opens a step, held to two lines for the same reason as .legal
   (2 x the inherited 1.5 line-height). */
.lead { min-height: 3em; }
/* A grouping wrapper the flex layout looks straight through: it exists so a
   whole mode's fields can be removed in one call, not to draw anything. */
.group { display: contents; }
/* The verify page's one action: sized to its label rather than the card,
   because the card's full width reads as a form submit, not a next step. */
.verify-next { display: flex; justify-content: center; margin: .6rem 0 .4rem; }
.verify-next .btn { width: auto; min-height: 44px; padding: 0 1.8rem; font-size: .95rem; }
/* Inside the small cards the trailing link row needs its own air: the card's
   padding alone leaves it looking pressed against the bottom edge. */
.card .links { margin: .6rem 0 .4rem; }

/* ---------- language switcher: pinned bottom-right, opens upwards ---------- */
.langpick { position: fixed; right: 1.1rem; bottom: 1.1rem; z-index: 20; }
.lang-btn {
  display: inline-flex; align-items: center; gap: .4rem; cursor: pointer;
  padding: .5rem .7rem; border-radius: 999px;
  border: 1px solid var(--field-line); background: var(--panel); color: var(--ink-soft);
  font-family: inherit; font-size: .84rem; box-shadow: var(--shadow);
}
.lang-btn:hover { color: var(--ink); border-color: var(--ink-faint); }
.lang-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
.lang-btn .ic { width: 17px; height: 17px; }
.lang-btn .caret { width: 15px; height: 15px; transition: transform .15s; }
.lang-btn[aria-expanded="true"] .caret { transform: rotate(180deg); }
.lang-menu {
  position: absolute; right: 0; bottom: calc(100% + .45rem);
  margin: 0; padding: .35rem; list-style: none;
  min-width: 11rem; max-height: 60vh; overflow-y: auto;
  border: 1px solid var(--line); border-radius: 12px;
  background: var(--panel); box-shadow: var(--shadow);
}
.lang-menu li { display: block; }
.lang {
  display: block; width: 100%; text-align: start; cursor: pointer;
  border: none; background: none; padding: .45rem .6rem; border-radius: 8px;
  font-family: inherit; font-size: .88rem; color: var(--ink-soft);
}
.lang:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 6%, transparent); }
.lang.on { color: var(--ink); font-weight: 600; }
.lang.on::after { content: " \2713"; color: var(--accent-text); }
.lang:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }

/* ---------- signup: field rows, choices, consents ---------- */
/* Paired fields sit side by side until the pair would be narrower than a date
   input can draw itself, then they wrap to one per line. The basis does that
   without a viewport breakpoint, which matters because the same row is wider on
   the sign-up column than inside an account card: at 360px the card left each
   half 133px, and the date field rendered as "dd/mm/yyy" with no calendar
   button. Equal basis and equal grow keep the halves equal when they do fit. */
/* flex-direction is stated because a form can carry .row itself, and the
   global form rule would otherwise leave it a column — which turned a
   field-and-button pair into a stack with a 10rem-tall button. */
.row { display: flex; flex-direction: row; flex-wrap: wrap; gap: .6rem; align-items: flex-end; }
.row > .field { flex: 1 1 10rem; min-width: 0; }
.row > .btn { flex: 1 1 10rem; min-width: 0; }
/* Verification actions get their own stack: side by side they overflow the
   380px column once the labels are translated. */
.row-stack { flex-direction: column; align-items: stretch; }
/* .row's 10rem flex-basis is written for the horizontal case; stacked, the
   main axis is vertical and the same basis becomes a 10rem-tall button. */
.row-stack > .btn { flex-basis: auto; }
/* Locales that write the family name first get the DOM order reversed visually,
   so one template serves both conventions. */
/* The name order lives in the markup (see _namerow.html), so that Tab and the
   eye agree. Reversing here would put them back at odds. */
/* A select clips its chosen option mid-glyph when the text outruns the box, and
   several of these do — "Follow browser language" is longer than the column it
   sits in. Ellipsis at least says so. Metrics mirror .field input, because a
   select shares a row with an input (gender beside birth date) and a shorter
   box left the pair visibly misaligned; appearance: none is what makes the
   browser honour those metrics at all — Safari's native pop-up ignores them —
   so the arrow is drawn here (--chevron) rather than left to the platform. */
.field select {
  width: 100%; min-height: 50px; padding: 0 2.2rem 0 .9rem;
  border: 1px solid var(--field-line); border-radius: 12px;
  background-color: var(--field); color: var(--ink);
  background-image: var(--chevron);
  background-repeat: no-repeat;
  background-position: right .85rem center;
  background-size: .7rem auto;
  font-size: 1rem; font-family: inherit; font-weight: 400;
  text-overflow: ellipsis;
  transition: border-color .15s ease, box-shadow .15s ease;
  -webkit-appearance: none; appearance: none;
}
.field select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--ring); }
/* One birth-date box (see _daterow.html): the visible input carries the
   digits in the locale's written order, the native input underneath is
   invisible and supplies the calendar; the button beside them opens it. */
.date-wrap { position: relative; display: flex; }
.date-wrap .date-text { flex: 1 1 auto; min-width: 0; padding-right: 3rem; }
.date-wrap .date-native {
  position: absolute; inset: 0; opacity: 0;
  pointer-events: none; border: 0; padding: 0;
}
.check { display: flex; align-items: start; gap: .5rem; cursor: pointer; font-size: .88rem; color: var(--ink); }
.check input { accent-color: var(--moa); width: 1rem; height: 1rem; margin: .15rem 0 0; flex: 0 0 auto; }
.consents { display: flex; flex-direction: column; gap: .5rem; margin: .2rem 0; }
.check span { line-height: 1.45; }
.check .req { color: var(--accent-text); font-weight: 600; }
.check a { color: var(--ink-soft); margin-inline-start: .3rem; }
.btn-unavailable { opacity: .5; cursor: not-allowed; filter: grayscale(1); }
.btn-unavailable:active { transform: none; }
.soon { font-style: normal; font-size: .78rem; opacity: .85; margin-inline-start: .35rem; }

/* ---------- legal documents ---------- */
.doc { max-width: 46rem; margin: 0 auto; padding: 2.4rem 1.3rem 5rem; }
.doc-head { padding-bottom: 1.2rem; border-bottom: 1px solid var(--line); margin-bottom: 1.8rem; }
.doc-nav { display: flex; flex-wrap: wrap; gap: 1rem; margin-top: 1.1rem; font-size: .9rem; }
.doc-nav a { color: var(--ink-soft); text-decoration: none; }
.doc-nav a:hover { color: var(--accent-text); text-decoration: underline; }
.doc-nav a.on { color: var(--ink); font-weight: 600; }
.prose { color: var(--ink); font-size: .95rem; line-height: 1.75; }
.prose h1 { font-size: 1.5rem; margin: 0 0 1rem; }
.prose h2 { font-size: 1.08rem; margin: 2rem 0 .6rem; }
.prose h3 { font-size: .98rem; margin: 1.5rem 0 .5rem; color: var(--ink-soft); }
.prose p, .prose li { margin: .5rem 0; }
.prose ol, .prose ul { padding-inline-start: 1.4rem; }
.prose a { color: var(--accent-text); }
.prose .doc-date { color: var(--ink-soft); font-size: .88rem; }
.prose table {
  width: 100%; border-collapse: collapse; margin: 1rem 0;
  font-size: .86rem; display: block; overflow-x: auto;
}
.prose th, .prose td { border: 1px solid var(--line); padding: .5rem .6rem; text-align: start; vertical-align: top; }
.prose th { background: var(--info-bg); font-weight: 600; }

/* ---------- links / legal / message ---------- */
.links {
  display: flex; align-items: center; justify-content: center; gap: .6rem;
  font-size: .9rem; margin: .4rem 0 0;
}
.links a { color: var(--ink-soft); text-decoration: none; }
.links a:hover { color: var(--accent-text); text-decoration: underline; }
.links span { color: var(--ink-faint); }
/* Two lines held open (2 x the 1.6 line-height below). The column is centred,
   so its height decides where everything in it sits: this note runs to one line
   in Korean and Chinese and two in the others, and letting that follow the
   language moved every button above it by half a line on each switch. */
.legal {
  margin: .6rem 0 0; text-align: center;
  font-size: .78rem; line-height: 1.6; color: var(--ink-faint);
  min-height: 3.2em;
}
.legal a { color: var(--ink-soft); }
.message {
  padding: .7rem .9rem; border-radius: 11px; font-size: .9rem;
  background: var(--info-bg); color: var(--ink);
}
.message.error { background: var(--error-bg); color: var(--error); }

/* ---------- simple card (verify / reset pages) ---------- */
.card {
  width: 100%; max-width: 380px; margin: 6vh auto;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 18px; padding: 2rem; box-shadow: var(--shadow);
}
.card h1 { font-size: 1.35rem; margin: 0 0 1rem; text-align: center; }

/* ---------- error pages ---------- */
/* The status code is the picture, drawn in the brand red fading toward the
   ground so it reads as scenery rather than alarm; the words below carry the
   actual message. */
.err-card { text-align: center; }
.err-code {
  margin: 1.2rem 0 .4rem; line-height: 1;
  font-size: clamp(4.2rem, 18vw, 5.6rem); font-weight: 800; letter-spacing: .03em;
  color: transparent;
  background: linear-gradient(160deg, var(--moa) 20%, color-mix(in srgb, var(--moa) 35%, var(--ground)));
  -webkit-background-clip: text; background-clip: text;
}
.err-card h1 { margin: 0 0 .5rem; }
.err-text { margin: 0 0 1.4rem; font-size: .92rem; line-height: 1.6; color: var(--ink-soft); }
.err-home { display: inline-flex; width: auto; padding-inline: 1.6rem; }

@media (prefers-reduced-motion: no-preference) {
  .view:not([hidden]) { animation: viewIn .22s ease both; }
}
@keyframes viewIn {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, ::before, ::after { transition: none !important; animation: none !important; }
}

/* ---- account page ---- */
.col-wide { max-width: 560px; }
/* A form that reads top-down and grows tall: centring one makes it float and
   re-place itself whenever its height changes — with the account tab, or with a
   consent line that wraps in one language and not another. Pin it to the top.
   Short views stay centred; their variable lines are held open instead. */
.auth-top { align-items: flex-start; }
.acct-head {
  display: grid; grid-template-columns: 1fr auto; align-items: center;
  gap: 1rem; margin-bottom: 1.1rem; text-align: left;
}
.acct-head .logo { margin: 0; }
.acct-signout { width: auto; padding: 0 .6rem; white-space: nowrap; }
/* The services live in a quiet footer, not the masthead. */
.svc-foot {
  display: flex; gap: .6rem; justify-content: center; align-items: center;
  margin: .4rem 0 .6rem; color: var(--ink-faint); font-size: .85rem;
}
.svc-foot a { color: var(--ink-soft); text-decoration: none; font-weight: 600; padding: .25rem .45rem; border-radius: 8px; }
.svc-foot a:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 6%, transparent); }

.field input:disabled, .field select:disabled { opacity: .55; cursor: not-allowed; }

/* Save/cancel stand clear of the last field. */
.form-actions { margin-top: .8rem; }

/* Email as one line, sign-up style: the address takes the width, the button
   keeps its own. The Verified state is a state, not a dead button. */
#email-form { flex-wrap: nowrap; }
#email-form .field { flex: 1 1 auto; }
/* The fixed minimum keeps the button the same size whichever word it wears. */
#email-form .btn { flex: 0 0 auto; width: auto; min-width: 7.2rem; min-height: 50px; padding: 0 1rem; }
.btn-verified { background: color-mix(in srgb, #2e9e5b 12%, transparent); color: #2e9e5b; border-color: transparent; }
.btn-verified:disabled { opacity: 1; cursor: default; }
/* A state, not a control: nothing to promise on hover. */
.btn-verified:hover { background: color-mix(in srgb, #2e9e5b 12%, transparent); border-color: transparent; }
.btn-verified:active { transform: none; }

/* Tab panels slide in briefly on switch; the URL changes with them. */
@keyframes panel-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}
.panel-in { animation: panel-in .18s ease; }
@media (prefers-reduced-motion: reduce) { .panel-in { animation: none; } }

/* ---- tabs ---- */
.tabs {
  position: relative;
  display: flex; gap: .3rem; margin-bottom: 1rem;
  border: 1px solid var(--field-line); border-radius: 12px; padding: .25rem;
  background: var(--panel);
}
.tab {
  position: relative; z-index: 1;
  flex: 1 1 0; border: none; background: none; cursor: pointer;
  font: inherit; font-size: .9rem; font-weight: 600; color: var(--ink-soft);
  padding: .55rem .4rem; border-radius: 9px;
  transition: color .2s ease;
}
.tab:hover { color: var(--ink); }
.tab.on { color: var(--ink); }
.tab:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
/* The highlight is one pill that glides to the pressed tab — a touch of
   overshoot in the curve gives it the springy feel without any library. */
.tab-pill {
  position: absolute; top: .25rem; bottom: .25rem; left: .25rem; width: 0;
  border-radius: 9px; background: color-mix(in srgb, var(--ink) 7%, transparent);
  transition: left .32s cubic-bezier(.3, 1.25, .35, 1), width .32s cubic-bezier(.3, 1.25, .35, 1);
}
@media (prefers-reduced-motion: reduce) { .tab-pill { transition: none; } }

/* ---- profile read/edit views ---- */
/* The card's own gap stops at its direct children, and both views are single
   wrappers — without their own rhythm the edit title sat flush against the
   120px avatar. */
#profile-view, #profile-edit { display: flex; flex-direction: column; gap: .85rem; }
#profile-edit h2 { margin-bottom: .25rem; }
.pv-head { display: flex; align-items: center; gap: 1.1rem; }
.pv-who { flex: 1 1 auto; min-width: 0; }
/* Names and addresses have no ceiling — 40-rune name parts, long mailboxes —
   so both wrap inside their column instead of squeezing whatever sits after
   them out of the card. The edit button pins to the top so growing text
   lengthens the card downwards, never into the button. */
.pv-name { margin: 0 0 .3rem; font-size: 1.2rem; font-weight: 700; overflow-wrap: anywhere; }
.pv-head .btn-slim { align-self: flex-start; }
.btn-slim { width: auto; min-height: 40px; padding: 0 1rem; font-size: .88rem; flex: 0 0 auto; }
.pv-list { margin: .4rem 0 0; display: flex; flex-direction: column; }
.pv-list > div {
  display: flex; gap: 1rem; padding: .5rem 0; font-size: .92rem;
  border-bottom: 1px solid var(--field-line);
}
.pv-list > div:last-child { border-bottom: none; }
.pv-list dt { flex: 0 0 8rem; color: var(--ink-soft); font-weight: 600; }
.pv-list dd { margin: 0; flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }

/* ---- avatar ---- */
.avatar-box { position: relative; flex: 0 0 auto; width: 96px; height: 96px; }
.avatar {
  display: block; width: 100%; height: 100%; border-radius: 50%;
  object-fit: cover; border: 1px solid var(--field-line); background: var(--field);
}
.avatar-fallback { position: absolute; inset: 0; background: color-mix(in srgb, var(--ink) 8%, transparent); }
/* Edit view: the centred photo IS the upload button; a camera strip along the
   bottom arc says so, and the remove link sits quietly beneath. */
.avatar-edit-wrap { display: flex; flex-direction: column; align-items: center; gap: .35rem; }
.avatar-edit-wrap .avatar-box { width: 120px; height: 120px; }
.avatar-clickable { cursor: pointer; display: block; border-radius: 50%; overflow: hidden; }
.avatar-clickable:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
.avatar-overlay {
  position: absolute; left: 0; right: 0; bottom: 0; height: 30%;
  display: grid; place-items: center;
  background: rgba(0, 0, 0, .45); color: #fff;
}
.avatar-overlay svg { width: 17px; height: 17px; }
.avatar-clickable:hover .avatar-overlay { background: rgba(0, 0, 0, .6); }

/* ---- social tiles ---- */
#social-tiles .btn { justify-content: flex-start; padding-left: 52px; }
#social-tiles .tile-state {
  margin-left: auto; font-style: normal; font-size: .78rem; opacity: .85;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 45%;
}
#social-tiles .btn.linked { box-shadow: inset 0 0 0 2px color-mix(in srgb, #2e9e5b 55%, transparent); }

/* ---- toggle switch ---- */
.switch-row { display: flex; align-items: center; gap: 1rem; cursor: pointer; }
.switch-label { flex: 1 1 auto; font-size: .92rem; }
.switch { position: relative; flex: 0 0 auto; width: 46px; height: 27px; }
.switch input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
.switch i {
  position: absolute; inset: 0; border-radius: 999px; pointer-events: none;
  background: color-mix(in srgb, var(--ink) 18%, transparent);
  transition: background-color .15s ease;
}
.switch i::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: 21px; height: 21px;
  border-radius: 50%; background: #fff; transition: transform .15s ease;
  box-shadow: 0 1px 2px rgba(0,0,0,.25);
}
.switch input:checked + i { background: #2e9e5b; }
.switch input:checked + i::after { transform: translateX(19px); }
.switch input:focus-visible + i { box-shadow: 0 0 0 3px var(--ring); }

/* ---- delete modal ---- */
.modal {
  border: 1px solid var(--field-line); border-radius: 14px; padding: 1.3rem;
  background: var(--panel); color: var(--ink); max-width: 26rem; width: calc(100vw - 3rem);
}
.modal::backdrop { background: rgba(0,0,0,.45); }
.modal h2 { margin: 0 0 .6rem; font-size: 1.05rem; }
.modal input {
  width: 100%; min-height: 46px; padding: 0 .9rem; margin: .6rem 0 .9rem;
  border: 1px solid var(--field-line); border-radius: 11px;
  background: var(--field); color: var(--ink); font: inherit;
  letter-spacing: .12em;
}
.card {
  background: var(--panel); border: 1px solid var(--field-line); border-radius: 14px;
  padding: 1.2rem 1.3rem 1.3rem; margin-bottom: 1rem;
  display: flex; flex-direction: column; gap: .7rem;
}
.card h2 { margin: 0; font-size: 1.05rem; }
.card h3 { margin: .8rem 0 0; font-size: .92rem; color: var(--ink-soft); display: flex; align-items: center; gap: .5rem; }
.acct-line { margin: 0; font-size: .95rem; display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.acct-line > span { min-width: 0; overflow-wrap: anywhere; }
.badge {
  font-style: normal; font-size: .72rem; padding: .1rem .5rem; border-radius: 999px;
  background: color-mix(in srgb, var(--ink) 8%, transparent); color: var(--ink-soft);
}
.badge-ok { background: color-mix(in srgb, #2e9e5b 15%, transparent); color: #2e9e5b; }
.acct-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.acct-list li {
  display: flex; align-items: center; gap: .6rem; padding: .5rem 0;
  border-bottom: 1px solid var(--field-line); font-size: .92rem;
}
.acct-list li:last-child { border-bottom: none; }
.acct-list li span { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.acct-list li em { font-style: normal; color: var(--ink-faint); font-size: .82rem; }
.acct-list li.empty { color: var(--ink-faint); border-bottom: none; }
.link-danger {
  background: none; border: none; cursor: pointer; font: inherit; font-size: .85rem;
  color: var(--error, #c23b2e); padding: .2rem .3rem;
}
.link-danger:hover { text-decoration: underline; }
.totp-secret, .recovery-codes {
  margin: 0; padding: .6rem .8rem; border-radius: 10px; border: 1px dashed var(--field-line);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .88rem;
  user-select: all; overflow-wrap: anywhere; white-space: pre-wrap;
}
.card-danger { border-color: color-mix(in srgb, var(--error, #c23b2e) 40%, transparent); }
.btn-danger { color: var(--error, #c23b2e); border-color: color-mix(in srgb, var(--error, #c23b2e) 45%, transparent); }
.btn-danger:hover { border-color: var(--error, #c23b2e); }
.btn-danger-solid { background: var(--error, #c23b2e); color: #fff; }
.btn-danger-solid:hover { background: color-mix(in srgb, var(--error, #c23b2e) 88%, black); }

/* ---- admin console ---- */
/* The operator's own address, said once at the top: every action below is
   written down under it, and the line is there so nobody forgets whose name
   the audit rows will carry. */
.adm-banner {
  margin: -.3rem 0 1rem; padding: .55rem .9rem; border-radius: 11px;
  font-size: .88rem; color: var(--ink-soft);
  background: var(--info-bg); border: 1px dashed var(--field-line);
  overflow-wrap: anywhere;
}
.adm-banner:empty { display: none; }
.adm-badges { display: inline-flex; flex-wrap: wrap; gap: .35rem; }
.badge-bad { background: var(--error-bg); color: var(--error); }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .86rem; }
/* Sessions and events are wide by nature — a user agent alone runs to 160
   runes — so each table scrolls inside its own box rather than pushing the
   card past the column. Cells stay on one line so the table reads as
   columns; the one or two free-text cells per row are allowed to break. */
.adm-scroll { overflow-x: auto; max-width: 100%; }
.adm-table { width: 100%; border-collapse: collapse; font-size: .82rem; }
.adm-table th, .adm-table td {
  padding: .4rem .5rem; text-align: start; vertical-align: top;
  border-bottom: 1px solid var(--field-line); white-space: nowrap;
}
.adm-table th { color: var(--ink-soft); font-weight: 600; }
.adm-table td.wrap { white-space: normal; overflow-wrap: anywhere; min-width: 14rem; }
.adm-table td.empty { color: var(--ink-faint); }
.adm-table tbody tr:last-child td { border-bottom: none; }
/* One action per line: these are not a set to scan but a list to read, and a
   destructive button deserves the full width of the card. The suspend form
   keeps its reason beside the button, sign-up style. */
.adm-actions { display: flex; flex-direction: column; gap: .5rem; }
#suspend-form { flex-wrap: nowrap; }
#suspend-form .field { flex: 1 1 auto; }
#suspend-form .btn { flex: 0 0 auto; width: auto; min-height: 50px; padding: 0 1.1rem; }
.adm-actions .btn:disabled, #suspend-form .btn:disabled { opacity: .45; }
#gone .btn { margin-top: .4rem; }
