/* =====================================================================
   Jono — getjono.com
   One stylesheet for the whole site: public pages, barber account area.

   One webfont, DM Sans, served from this origin. Nothing is fetched from a
   host we do not control, which is what lets the privacy policy say there
   are no third parties in the page and lets the site run with no consent
   banner. Google Fonts' CDN would break that; the file is in assets/fonts/
   instead.

   Palette is the app's: BRAND #22C55E from src/lib/theme.tsx, with the
   neutrals already used by privacy.html so the two do not look like
   different products.
   ===================================================================== */

/* ---------------------------------------------------------------------
   Frosted Mint — the brand surface, light first.

   Two families of token. The flat ones (--bg, --card, --line) are what a
   surface uses when it must be opaque: an input, a table row, anything a
   reader's eye has to resolve text against. The --glass-* ones are the
   frosted recipe, and they only work over the ground painted on <body>;
   putting them on a surface with something busy behind it is how glass
   turns into fog.

   The palette is the app's — src/lib/theme.tsx carries the same numbers, so
   the two do not drift into looking like different products.
   --------------------------------------------------------------------- */
:root {
  --bg: #f4f6f5;
  --fg: #12291d;
  --muted: #5b6660;
  --line: #dbe5df;
  --card: #ffffff;
  --brand: #16a34a;
  --brand-bright: #22c55e;
  --brand-ink: #ffffff;
  --brand-soft: #e7f8ee;
  --link: #14532d;
  --warn-bg: #fff8e6;
  --warn-line: #e8d9a8;
  --err: #b91c1c;
  --radius: 14px;
  --shadow: 0 1px 2px rgba(18, 41, 29, .05), 0 10px 28px -14px rgba(18, 41, 29, .16);

  --ground: linear-gradient(160deg, #eaf5ee 0%, #cdeed9 45%, #f4f6f5 100%);
  --glow-a: rgba(34, 197, 94, .18);
  --glow-b: rgba(34, 197, 94, .15);
  --glass-bg: rgba(255, 255, 255, .55);
  --glass-line: rgba(255, 255, 255, .7);
  --glass-inset: inset 0 1px 0 rgba(255, 255, 255, .8);
  --glass-blur: saturate(180%) blur(24px);

  /* Chevron Flow's three stops, dim to bright. */
  --mark-1: #2a342d;
  --mark-2: #4b5a50;
  --mark-3: #22c55e;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f1511;
    --fg: #f3f4f6;
    --muted: #9ca3af;
    --line: #2a342d;
    --card: #171f1a;
    --brand: #22c55e;
    --brand-ink: #062611;
    --brand-soft: rgba(34, 197, 94, .16);
    --link: #4ade80;
    --warn-bg: #211d10;
    --warn-line: #4a3f1d;
    --err: #f87171;
    --shadow: none;

    --ground: linear-gradient(160deg, #0d2b1c 0%, #14532d 45%, #0a1f14 100%);
    --glow-a: rgba(255, 255, 255, .08);
    --glow-b: rgba(34, 197, 94, .30);
    --glass-bg: rgba(255, 255, 255, .07);
    --glass-line: rgba(255, 255, 255, .19);
    --glass-inset:
      inset 0 1px 0 rgba(255, 255, 255, .28),
      inset 0 -1px 0 rgba(34, 197, 94, .18);
    --glass-blur: saturate(190%) blur(32px);

    /* `#2a342d` *is* the dark hairline colour, so on the dark ground the
       first stroke would be a mark nobody can see rather than a dim one. */
    --mark-1: #3e4b43;
    --mark-2: #6c7f73;
  }
}

/* ---------------------------------------------------------------------
   DM Sans, the brand typeface — self-hosted, and that is the whole point.
   One variable file carries every weight from 100 to 1000, so the page
   never asks a font CDN for anything and the "no third parties" promise in
   the privacy policy stays literally true. `font-src 'self'` in .htaccess
   would refuse a remote face anyway.

   `font-display: swap` means text paints immediately in the fallback and
   reflows when the face arrives — 88 KB is small, but a blank page while a
   font loads is never the right trade.

   Licensed under the SIL OFL; see fonts/OFL.txt, which ships beside the
   file because the licence requires it to travel with the font.
   --------------------------------------------------------------------- */
@font-face {
  font-family: 'DM Sans';
  src: url('fonts/DMSans-Variable.woff2') format('woff2');
  font-weight: 100 1000;
  font-stretch: normal;
  font-style: normal;
  font-display: swap;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font: 16px/1.65 'DM Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* The Frosted Mint ground: one fixed layer under everything, carrying the
   diagonal wash and the two soft glow blobs. `position: fixed` so it does not
   scroll with the page and does not grow the document; `z-index: -1` so it
   stays behind content without needing a stacking context on every section.

   The blobs are radial gradients rather than blurred circles — a 60 px blur
   filter on a fixed layer is repainted on every scroll on some browsers, and
   a gradient that fades to nothing is the same picture for free.

   They are large and centred just *outside* their corner, so what is on screen
   is the tail rather than the core. At 18rem centred inside the corner they
   read as two distinct light sources rather than as a wash — reported from the
   app, where the same numbers were in `Surface.tsx`, and fixed in both. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(52rem 52rem at 104% -4%, var(--glow-a), transparent 72%),
    radial-gradient(52rem 52rem at -4% 104%, var(--glow-b), transparent 72%),
    var(--ground);
}

/* ---------------------------------------------------------------------
   Glass. One rule, applied by name, so the recipe lives in exactly one
   place — see the token block at the top for what each part is.
   --------------------------------------------------------------------- */
.glass,
.topbar,
.card,
.note,
.langbar {
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-line);
  box-shadow: var(--glass-inset);
}

/* A browser with no `backdrop-filter` gets the flat card colour instead of a
   translucent pane over unblurred text, which is the one way this can fail
   badly rather than plainly. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .glass, .topbar, .card, .note, .langbar { background: var(--card); }
}

.wrap { max-width: 44rem; margin: 0 auto; padding: 2.5rem 1.25rem 5rem; }
.wrap-wide { max-width: 62rem; }

h1 { font-size: 1.85rem; line-height: 1.2; margin: 0 0 .35rem; letter-spacing: -.02em; }
h2 { font-size: 1.2rem; margin: 2.5rem 0 .6rem; letter-spacing: -.01em; }
h3 { font-size: 1rem; margin: 1.6rem 0 .4rem; }
p, li { color: var(--fg); }
a { color: var(--link); }
a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, textarea:focus-visible, summary:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

.muted { color: var(--muted); }
.lead { color: var(--muted); margin: 0 0 2rem; font-size: .95rem; }
.small { font-size: .88rem; }
hr { border: 0; border-top: 1px solid var(--line); margin: 3.5rem 0; }
code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .9em; }

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

.topbar {
  border-radius: 0;
  border-inline: 0;
  border-top: 0;
  border-bottom: 1px solid var(--glass-line);
  position: sticky;
  top: 0;
  z-index: 10;
}
.topbar-in {
  max-width: 62rem; margin: 0 auto; padding: .85rem 1.25rem;
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
}
.brand {
  font-weight: 700; letter-spacing: -.03em; font-size: 1.15rem;
  color: var(--fg); text-decoration: none; margin-right: auto;
  display: inline-flex; align-items: center; gap: .45rem;
}
/* Chevron Flow, inline in the markup so it inherits the page's colours and
   costs no request. The three strokes carry their own fill from the SVG. */
.brand-mark { width: 1.6rem; height: 1.6rem; flex: none; display: block; }
.topbar nav { display: flex; gap: 1.1rem; flex-wrap: wrap; font-size: .92rem; }
.topbar nav a { color: var(--muted); text-decoration: none; }
.topbar nav a:hover, .topbar nav a[aria-current="page"] { color: var(--fg); }

.foot {
  border-top: 1px solid var(--line);
  margin-top: 4rem; padding: 2rem 1.25rem 3rem;
  color: var(--muted); font-size: .88rem;
}
.foot-in { max-width: 62rem; margin: 0 auto; display: flex; gap: 1.2rem; flex-wrap: wrap; }
.foot a { color: var(--muted); }

/* ---------- language toggle ---------- */

.langbar { display: inline-flex; border-radius: 999px; overflow: hidden; }
.langbar button {
  appearance: none; border: 0; background: transparent; color: var(--muted);
  font: inherit; font-size: .85rem; padding: .25rem .8rem; cursor: pointer;
}
.langbar button[aria-pressed="true"] { background: var(--brand); color: var(--brand-ink); font-weight: 600; }

/* Hidden until lang.js decides. Without JS, both languages show, stacked —
   which is the correct fallback for a legal page and an acceptable one for
   the landing page. */
[data-lang] { }
html.js [data-lang="en"] { display: none; }
html.js.lang-en [data-lang="en"] { display: revert; }
html.js.lang-en [data-lang="fi"] { display: none; }

/* ---------- hero ---------- */

.hero { padding: 4rem 1.25rem 3rem; }
.hero-in { max-width: 62rem; margin: 0 auto; }
.hero h1 { font-size: clamp(2rem, 5vw, 2.9rem); max-width: 18ch; }
.hero p.sub { font-size: 1.12rem; color: var(--muted); max-width: 46ch; margin: 1rem 0 2rem; }

.cta { display: flex; gap: .75rem; flex-wrap: wrap; align-items: center; }

.btn {
  appearance: none; display: inline-block; text-decoration: none; cursor: pointer;
  font: inherit; font-weight: 600; font-size: .95rem;
  padding: .7rem 1.25rem; border-radius: 12px;
  border: 1px solid var(--glass-line); color: var(--fg);
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-inset);
}
.btn:hover { border-color: var(--muted); }
/* The one solid thing on the surface. A call to action that dissolves into
   the pane behind it is not a call to action. */
.btn-primary {
  background: var(--brand); border-color: var(--brand); color: var(--brand-ink);
  -webkit-backdrop-filter: none; backdrop-filter: none; box-shadow: none;
}
.btn-primary:hover { filter: brightness(1.05); border-color: var(--brand); }
.btn-danger { border-color: var(--err); color: var(--err); background: transparent; }
.btn[disabled], .btn[aria-disabled="true"] { opacity: .55; cursor: not-allowed; }
.btn-sm { padding: .4rem .75rem; font-size: .85rem; }

/* ---------- cards & grids ---------- */

.grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }

.card {
  border-radius: var(--radius); padding: 1.15rem 1.25rem;
}
.card h3 { margin-top: 0; }
.card p:last-child { margin-bottom: 0; }

.note {
  border-radius: 12px; padding: .9rem 1rem; margin: 1.2rem 0; font-size: .93rem;
}
/* A warning is not a decoration: it drops the glass for a solid ground so it
   reads as an interruption rather than another pane. */
.note-warn {
  background: var(--warn-bg);
  border-color: var(--warn-line);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  box-shadow: none;
}

.steps { counter-reset: step; list-style: none; padding: 0; margin: 1.5rem 0; }
.steps li {
  counter-increment: step; position: relative;
  padding: 0 0 1.4rem 3rem; margin: 0;
}
.steps li::before {
  content: counter(step);
  position: absolute; left: 0; top: -.1rem;
  width: 2rem; height: 2rem; border-radius: 50%;
  background: var(--brand-soft); color: var(--brand);
  display: grid; place-items: center; font-weight: 700; font-size: .9rem;
  border: 1px solid var(--brand);
}
.steps li strong { display: block; }

/* Placeholder frames for imagery that does not exist yet. Deliberately
   visible rather than a blank space, so an empty slot reads as unfinished
   rather than as a design. */
.shot {
  border: 1px dashed var(--line); border-radius: var(--radius);
  background: var(--card); color: var(--muted);
  display: grid; place-items: center; text-align: center;
  padding: 1rem; font-size: .85rem; aspect-ratio: 16 / 10;
}
.shot-tall { aspect-ratio: 9 / 16; }

/* ---------- tables ---------- */

table { border-collapse: collapse; width: 100%; font-size: .92rem; margin: 1rem 0; }
th, td { text-align: left; vertical-align: top; padding: .6rem .7rem; border-bottom: 1px solid var(--line); }
th { font-weight: 600; }
thead th { border-bottom: 2px solid var(--line); }
.scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

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

.field { margin: 0 0 1rem; }
.field label { display: block; font-size: .88rem; font-weight: 600; margin: 0 0 .3rem; }
.field .hint { display: block; font-size: .82rem; color: var(--muted); margin: .25rem 0 0; }

input[type="text"], input[type="email"], input[type="password"],
input[type="tel"], input[type="datetime-local"], select, textarea {
  width: 100%; font: inherit; color: var(--fg);
  background: var(--bg); border: 1px solid var(--line);
  border-radius: 9px; padding: .6rem .7rem;
}
textarea { min-height: 6rem; resize: vertical; }

fieldset { border: 0; padding: 0; margin: 0; }

.row { display: flex; gap: .75rem; flex-wrap: wrap; align-items: flex-end; }
.row > * { flex: 1 1 12rem; }
.row > .shrink { flex: 0 0 auto; }

/* ---------- status messages ---------- */

.msg { border-radius: 10px; padding: .75rem .9rem; margin: 1rem 0; font-size: .92rem; border: 1px solid; }
.msg-ok { background: var(--brand-soft); border-color: var(--brand); color: var(--fg); }
.msg-err { background: transparent; border-color: var(--err); color: var(--err); }
.msg-info { background: var(--card); border-color: var(--line); color: var(--fg); }
.msg[hidden] { display: none; }

.badge {
  display: inline-block; font-size: .75rem; font-weight: 700; letter-spacing: .02em;
  text-transform: uppercase; padding: .15rem .5rem; border-radius: 999px;
  border: 1px solid var(--line); color: var(--muted);
}
.badge-ok { background: var(--brand-soft); border-color: var(--brand); color: var(--brand); }
.badge-warn { background: var(--warn-bg); border-color: var(--warn-line); color: var(--fg); }
.badge-err { border-color: var(--err); color: var(--err); }

.spinner { color: var(--muted); font-size: .9rem; }

/* ---------- account area ---------- */

.acct-nav { display: flex; gap: .4rem; flex-wrap: wrap; margin: 0 0 2rem; }
.acct-nav a {
  text-decoration: none; font-size: .9rem; color: var(--muted);
  padding: .35rem .75rem; border-radius: 999px; border: 1px solid transparent;
}
.acct-nav a[aria-current="page"] { color: var(--fg); border-color: var(--line); background: var(--card); }

.kv { display: grid; grid-template-columns: max-content 1fr; gap: .35rem 1rem; font-size: .93rem; margin: 1rem 0; }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; }

.code-out {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 1.3rem; letter-spacing: .06em; font-weight: 700;
  background: var(--brand-soft); border: 1px solid var(--brand);
  border-radius: 10px; padding: .8rem 1rem; text-align: center; user-select: all;
}

@media (max-width: 30rem) {
  .kv { grid-template-columns: 1fr; gap: .1rem .5rem; }
  .kv dd { margin-bottom: .6rem; }
}


/* ---------------------------------------------------------------------
   Chevron loading — the mark, waiting.

   The same animation as the app's `ChevronSpinner`: each stroke pulses in
   place, staggered, so brightness runs left to right. Nothing moves; a mark
   that slides is a different mark.

   `.chevron-load` is the full size with a caption under it, `.is-inline` the
   26 px variant for a button or a row that is mid-action.
   --------------------------------------------------------------------- */
.chevron-load {
  display: flex; flex-direction: column; align-items: center; gap: .9rem;
  padding: 2.5rem 1rem;
  border-radius: var(--radius);
  background: radial-gradient(circle at 30% 20%, #14311f, #0f1511 70%);
}
.chevron-load svg { width: 64px; height: 64px; display: block; }
.chevron-load .cap { color: #9ca3af; font-size: .8rem; margin: 0; }

.chevron-load.is-inline {
  flex-direction: row; gap: .5rem; padding: 0; background: none;
}
.chevron-load.is-inline svg { width: 26px; height: 26px; }
.chevron-load.is-inline .cap { color: var(--muted); }

.chevron-load path {
  stroke: var(--brand-bright, #22c55e);
  stroke-width: 5;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
  opacity: .2;
  animation: chevronFlow 1.4s ease-in-out infinite;
}
.chevron-load path:nth-child(2) { animation-delay: .15s; }
.chevron-load path:nth-child(3) { animation-delay: .3s; }
.chevron-load.is-inline path:nth-child(2) { animation-delay: .12s; }
.chevron-load.is-inline path:nth-child(3) { animation-delay: .24s; }

@keyframes chevronFlow {
  0%, 20%   { opacity: .2; filter: brightness(.7); }
  50%       { opacity: 1;  filter: brightness(1.4) drop-shadow(0 0 6px rgba(34, 197, 94, .7)); }
  80%, 100% { opacity: .2; filter: brightness(.7); }
}

/* Someone who has asked for less motion still needs to know the page is
   working, so the mark stays and only the pulse stops. */
@media (prefers-reduced-motion: reduce) {
  .chevron-load path { animation: none; opacity: 1; }
}

/* `hidden` is how the scripts dismiss these, and a class setting `display:
   flex` outranks the UA stylesheet's `[hidden] { display: none }` — without
   this the loader never goes away. */
.chevron-load[hidden] { display: none; }

