/* ==========================================================================
   vguliakin.dev — Monochrome Scanner
   Single stylesheet. No framework, no build step, no external resources.
   ========================================================================== */

:root {
  --bg:        #F5F5F2;   /* warm paper background */
  --ink:       #111214;   /* primary dark, matches the QR Scevio icon field */
  --paper:     #ECECE9;   /* light text on dark sections */
  --muted:     #4C4E54;   /* 7.6:1 on --bg, 7.1:1 on the callout ground */
  --muted-inv: #A7A9AE;   /* 7.4:1 on --ink */
  --line:      #D5D5D0;
  --line-inv:  #33363B;

  --measure: 43rem;       /* ~78 characters at body size */
  --page:    72rem;       /* outer container */
  --gutter:  clamp(1.25rem, 5vw, 3rem);
  --rule:    1px solid var(--line);

  /* Spacing scale. Use these instead of inventing values. */
  --s-1:  0.25rem;  /*   4 */
  --s-2:  0.5rem;   /*   8 */
  --s-3:  0.75rem;  /*  12 */
  --s-4:  1rem;     /*  16 */
  --s-5:  1.5rem;   /*  24 */
  --s-6:  2rem;     /*  32 */
  --s-7:  3rem;     /*  48 */
  --s-8:  4rem;     /*  64 */
  --s-9:  6rem;     /*  96 */
  --s-10: 8rem;     /* 128 */

  /* Type scale. Each step is a fluid range: mobile → desktop. */
  --step-0: clamp(1.0625rem, 1.02rem + 0.20vw, 1.1875rem);  /* body    17 → 19 */
  --step-1: clamp(1.1875rem, 1.10rem + 0.40vw, 1.375rem);   /* lede    19 → 22 */
  --step-2: clamp(1.75rem,   1.45rem + 1.30vw, 2.5rem);     /* h2      28 → 40 */
  --step-3: clamp(2.125rem,  1.70rem + 2.10vw, 3.25rem);    /* page h1 34 → 52 */
  --step-4: clamp(2.875rem,  2.10rem + 3.90vw, 4.25rem);    /* hero    46 → 68 */

  --font: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text",
          "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

*, *::before, *::after { box-sizing: border-box; }

html {
  /* Painted here too, so overscroll areas on iOS match the page. */
  background: var(--bg);
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--step-0);
  line-height: 1.65;
  letter-spacing: -0.003em;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3 {
  margin: 0;
  font-weight: 600;
  letter-spacing: -0.026em;
  line-height: 1.06;
  text-wrap: balance;
}
p { margin: 0; text-wrap: pretty; }
/* The UA gives <figure> a 40px inline margin, which was quietly shrinking every
   screenshot by 80px inside its column. */
figure { margin: 0; }

a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 0.22em; }
a:hover { text-decoration-color: var(--muted); }

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 2px;
}
.section--dark :focus-visible { outline-color: var(--paper); }

img { max-width: 100%; height: auto; display: block; }

/* --------------------------------------------------------------- layout -- */

.wrap {
  width: 100%;
  max-width: var(--page);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section {
  padding-block: clamp(var(--s-8), 9vw, var(--s-10));
  border-top: var(--rule);
}
.section:first-of-type { border-top: 0; }

.section--dark {
  --muted: var(--muted-inv);
  --line: var(--line-inv);
  background: var(--ink);
  color: var(--paper);
  border-top-color: var(--ink);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--ink);
  color: var(--paper);
  padding: 0.75rem 1rem;
  z-index: 10;
}
.skip-link:focus { left: 0; }

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

.masthead {
  border-bottom: var(--rule);
  position: relative;
}
.masthead__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 4.25rem;
  flex-wrap: wrap;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: -0.01em;
  text-decoration: none;
  padding-block: 0.75rem;
}
.brand__mark { width: 26px; height: 26px; border-radius: 5px; flex: none; }

.nav { display: flex; gap: clamp(1rem, 3vw, 1.75rem); }
.nav a {
  font-size: 0.9375rem;
  color: var(--muted);
  text-decoration: none;
  padding: 0.75rem 0;      /* 44px effective touch height with the flex row */
  display: inline-block;
}
.nav a:hover, .nav a[aria-current="page"] { color: var(--ink); }

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

.hero { padding-block: clamp(var(--s-8), 11vw, var(--s-10)); }

.hero__title {
  font-size: var(--step-4);
  max-width: 18ch;
}
.hero__lede {
  margin-top: var(--s-5);
  font-size: var(--step-1);
  color: var(--muted);
  max-width: 36rem;
}

.eyebrow {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 var(--s-4);
}

/* QR Scevio hero: mark beside the copy on wide screens */
.hero--product .hero__inner {
  display: grid;
  gap: clamp(var(--s-8), 7vw, var(--s-9));
  align-items: center;
}
@media (min-width: 56rem) {
  .hero--product .hero__inner {
    grid-template-columns: minmax(0, 1fr) auto;
  }
}
.hero--product .hero__title { font-size: var(--step-4); max-width: 11ch; }

/* Keep the corner marks hugging the icon rather than the whole grid column. */
.hero--product .brackets { width: max-content; margin-inline: auto; }

/* ------------------------------------------------------- scanner device -- */
/* Thin L-shaped corner marks. Used on exactly three elements site-wide.     */

.brackets { position: relative; }
.brackets::before,
.brackets::after {
  content: "";
  position: absolute;
  width: 20px;
  height: 20px;
  border: 1px solid var(--line);
  pointer-events: none;
}
/* Never let the mark push past the page gutter, or it clips off-screen on
   narrow viewports. */
.brackets {
  --bracket-inset: min(var(--s-5), calc(var(--gutter) - var(--s-2)));
}
.brackets::before { top: calc(-1 * var(--bracket-inset)); left: calc(-1 * var(--bracket-inset)); border-right: 0; border-bottom: 0; }
.brackets::after  { bottom: calc(-1 * var(--bracket-inset)); right: calc(-1 * var(--bracket-inset)); border-left: 0; border-top: 0; }

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

.mark {
  position: relative;
  width: clamp(10rem, 26vw, 15rem);
  aspect-ratio: 1;
  background: var(--ink);
  border-radius: 22.37%;   /* iOS squircle approximation */
  overflow: hidden;
  flex: none;
  margin-inline: auto;
}
.mark svg { width: 100%; height: 100%; display: block; }

/* The one permitted animation: a slow, thin scan line across the mark.
   Opt-in only — it exists solely inside a no-preference media query, so
   reduced-motion users get the static state by default, not as an override. */
@media (prefers-reduced-motion: no-preference) {
  .mark::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    top: 0;
    height: 1px;
    background: var(--paper);
    opacity: 0.28;
    animation: scan 7s cubic-bezier(0.45, 0, 0.55, 1) infinite;
  }
  @keyframes scan {
    0%, 8%    { transform: translateY(4%);  opacity: 0; }
    18%       { opacity: 0.28; }
    82%       { opacity: 0.28; }
    92%, 100% { transform: translateY(96%); opacity: 0; }
  }
}

/* -------------------------------------------------------------- buttons -- */

.actions {
  margin-top: var(--s-7);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-4) var(--s-6);
}

.cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 3rem;
  padding: var(--s-3) var(--s-5);
  border-radius: 6px;
  font-size: 1rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  text-decoration: none;
}

/* Live App Store button. Unused until the URL exists — see README. */
.cta {
  background: var(--ink);
  color: var(--paper);
  border: 1px solid var(--ink);
}
.cta:hover { background: #000; }
.section--dark .cta { background: var(--paper); color: var(--ink); border-color: var(--paper); }

/* Not-yet-available state. Deliberately a <span>: nothing to activate, so it
   must not sit in the tab order pretending to be a link, and no box either —
   a bordered one reads as a disabled control the user should be able to press. */
.cta--pending {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  min-height: 3rem;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.cta--pending::before {
  content: "";
  width: var(--s-5);
  height: 1px;
  background: currentColor;
  opacity: 0.55;
}

.textlink {
  font-size: 0.9375rem;
  color: var(--muted);
  text-underline-offset: 0.25em;
  display: inline-block;
  padding-block: var(--s-3);   /* 49px target height */
}
.textlink:hover { color: var(--ink); }
.section--dark .textlink:hover { color: var(--paper); }

/* ------------------------------------------------------------- headings -- */

.section__title {
  font-size: var(--step-2);
  max-width: 18ch;
}
.section__lede {
  margin-top: var(--s-4);
  color: var(--muted);
  max-width: 36rem;
  font-size: var(--step-1);
}

/* ------------------------------------------------------- scan · inspect -- */

.steps {
  margin-top: var(--s-7);
  display: grid;
  gap: 0;
  border-top: var(--rule);
}
@media (min-width: 48rem) {
  .steps { grid-template-columns: repeat(3, 1fr); }
}
.step {
  padding: var(--s-6) 0;
  border-bottom: var(--rule);
}
@media (min-width: 48rem) {
  .step { border-bottom: 0; padding-inline: clamp(var(--s-5), 3vw, var(--s-6)); border-left: var(--rule); }
  .step:first-child { padding-left: 0; border-left: 0; }
  .step:last-child  { padding-right: 0; }
}
.step__index {
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--muted);
  letter-spacing: 0.08em;
}
.step__name {
  margin: var(--s-3) 0 var(--s-2);
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.step p { color: var(--muted); max-width: 32ch; }

/* -------------------------------------------------------------- privacy -- */

.facts {
  list-style: none;
  margin: var(--s-7) 0 0;
  padding: 0;
  display: grid;
  gap: 0;
  border-top: var(--rule);
  max-width: 44rem;
}
.facts li {
  padding: var(--s-5) 0;
  border-bottom: var(--rule);
  display: grid;
  grid-template-columns: var(--s-5) 1fr;
  gap: var(--s-2);
  align-items: baseline;
  font-size: var(--step-1);
  line-height: 1.45;
}
/* Decorative only — the meaning is entirely in the text beside it.
   A corner fragment rather than a closed square: a thin 8px outlined box is
   indistinguishable from the missing-glyph tofu character, and readers assume
   the page failed to render. This matches the scanner marks in .brackets. */
.facts li::before {
  content: "";
  width: 9px;
  height: 9px;
  border-left: 1px solid currentColor;
  border-top: 1px solid currentColor;
  opacity: 0.55;
  transform: translateY(-2px);
}
.note {
  margin-top: var(--s-6);
  font-size: 0.9375rem;
  color: var(--muted);
  max-width: 36rem;
}

/* ---------------------------------------------------------- screenshots -- */

.shots {
  margin-top: var(--s-8);
  display: grid;
  gap: var(--s-8) clamp(var(--s-5), 4vw, var(--s-7));
  align-items: start;
  max-width: 62rem;
}
/* Stacked, three full-height phone screenshots would be an enormous scroll.
   Cap them until there is room for the three-column layout. */
@media (max-width: 45.999rem) {
  .shot img { max-width: 19rem; }
}
@media (min-width: 46rem) {
  .shots { grid-template-columns: repeat(3, 1fr); }
}
/* The screenshot is the content. The frame stays a hairline — no card, no
   shadow, no device mockup. */
.shot img {
  width: 100%;
  border: var(--rule);
  border-radius: 2px;
  background: #fff;
}
.shot__step {
  margin-top: var(--s-5);
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  color: var(--muted);
}
.shot__title {
  margin-top: var(--s-2);
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.012em;
}
.shot figcaption {
  margin-top: var(--s-2);
  font-size: 0.9375rem;
  color: var(--muted);
  max-width: 34ch;
}

/* --------------------------------------------------------------- bottom -- */

.closer { text-align: center; }
.closer .section__title { margin-inline: auto; }
.closer__tag {
  margin-top: var(--s-4);
  font-family: var(--mono);
  font-size: 0.875rem;
  letter-spacing: 0.06em;
  color: var(--muted);
}
.closer .actions { justify-content: center; margin-top: var(--s-6); }
.closer .note { margin-inline: auto; }

/* ------------------------------------------------------- project teaser -- */

.contact-line { margin-top: var(--s-5); }

.project {
  margin-top: var(--s-7);
  display: grid;
  gap: clamp(var(--s-5), 4vw, var(--s-7));
  align-items: center;
  border-top: var(--rule);
  border-bottom: var(--rule);
  padding-block: var(--s-7);
}
@media (min-width: 40rem) {
  .project { grid-template-columns: auto minmax(0, 1fr); }
}
.project__icon { width: 88px; height: 88px; border-radius: 19.7px; flex: none; }
.project__name { font-size: var(--step-2); }
.project__name a { text-decoration: none; }
.project__name a:hover { text-decoration: underline; }
.project p { margin-top: var(--s-3); color: var(--muted); max-width: 36rem; }

/* ---------------------------------------------------------------- prose -- */

.prose {
  max-width: var(--measure);
  /* Matches .section's bottom padding so every page meets the footer with the
     same gap. Long-form pages have no .section wrapper to provide it. */
  padding-bottom: clamp(var(--s-8), 9vw, var(--s-10));
}
.prose > * + * { margin-top: var(--s-4); }
.prose h2 {
  margin-top: var(--s-8);
  font-size: 1.5rem;
  scroll-margin-top: var(--s-5);
}
/* Only the handful of real group boundaries carry a rule. */
.prose h2.prose--group {
  margin-top: var(--s-9);
  padding-top: var(--s-7);
  border-top: var(--rule);
}
.prose h3 {
  margin-top: var(--s-6);
  font-size: 1.0625rem;
  font-weight: 600;
  scroll-margin-top: var(--s-5);
}
.prose ul { margin-top: var(--s-4); padding-left: var(--s-4); }
.prose li { margin-top: var(--s-2); }
.prose li::marker { color: var(--muted); }
.prose code {
  font-family: var(--mono);
  font-size: 0.9em;
  background: #E9E9E4;
  padding: 0.1em 0.35em;
  border-radius: 2px;
}
.prose strong { font-weight: 600; }

.page-head { padding-block: clamp(var(--s-7), 8vw, var(--s-9)) var(--s-5); }
.page-head h1 { font-size: var(--step-3); }
.page-head .meta {
  margin-top: var(--s-4);
  font-size: 0.9375rem;
  color: var(--muted);
}

/* ------------------------------------------------------------------ toc --
   Plain anchor list for the two long pages. No sticky behaviour, no script.  */

.toc {
  margin-bottom: var(--s-8);
  padding-block: var(--s-5);
  border-block: var(--rule);
  max-width: var(--measure);
}
.toc h2 {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 var(--s-3);
}
.toc ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--s-5);
}
.toc a {
  display: inline-block;
  padding-block: var(--s-3);   /* 49px target height */
  font-size: 0.9375rem;
  color: var(--muted);
  text-decoration: none;
}
.toc a:hover { color: var(--ink); text-decoration: underline; }

.callout {
  margin-top: var(--s-6);
  padding: var(--s-5) var(--s-6);
  border: var(--rule);
  border-left: 2px solid var(--ink);
  border-radius: 2px;
  background: #EFEFEB;
}
.callout h2 {
  margin: 0 0 var(--s-3);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}
.callout p + p { margin-top: var(--s-3); }
.callout ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--s-2);
}

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

.footer {
  border-top: var(--rule);
  padding-block: clamp(var(--s-7), 6vw, var(--s-8));
  font-size: 0.9375rem;
}
.footer__name {
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.012em;
}
.footer__links {
  list-style: none;
  margin: var(--s-2) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 var(--s-5);
}
/* No interpunct separators here on purpose: when the row wraps, a pseudo-
   element separator orphans onto the start of the next line. Spacing alone
   reads correctly at every width. */
.footer a { color: var(--muted); text-decoration: none; display: inline-block; padding-block: var(--s-3); }
.footer a:hover { color: var(--ink); text-decoration: underline; }
.footer__legal {
  margin-top: var(--s-7);
  padding-top: var(--s-5);
  border-top: var(--rule);
  color: var(--muted);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-5);
  justify-content: space-between;
}

/* ------------------------------------------------------------------ 404 -- */

.error { padding-block: clamp(var(--s-9), 14vw, var(--s-10)) clamp(var(--s-8), 9vw, var(--s-10)); }
.error h1 { font-size: var(--step-3); }
.error p { margin-top: 1.25rem; color: var(--muted); }

/* -------------------------------------------------------------- utility -- */

.muted { color: var(--muted); }
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

@media print {
  .masthead, .footer, .actions, .toc { display: none; }
  body { background: #fff; color: #000; }
}
