/* efaimo.ai: the one stylesheet for its six pages (English at the root, Korean
   under /ko/), and the only request a page makes besides its icons and the
   Host Grotesk file.
   Colours are the ink ladder of pixel-apps/design/DESIGN.md on a white ground.
   Text uses --ink, --text or --muted, all above 4.5:1 on white and on --panel;
   measure with efaimo-ai/tools/contrast.mjs after touching any of them.
   English pages read in Host Grotesk, the face efaimo-site sets its text in,
   from the same self-hosted file, so the CSP stays at 'self'. The file is a
   Latin subset with no Hangul, so Korean pages read in the system face and
   keep Host Grotesk for the wordmark. */

@font-face {
  font-family: "Host Grotesk";
  src: url("/fonts/HostGrotesk-Variable.woff2") format("woff2");
  font-weight: 300 800;
  font-style: normal;
  font-display: swap;
}

:root {
  color-scheme: light;
  --ink: #16181d;
  --text: #3a3f48;
  --muted: #5a6d76;
  --rule: #ebecef;
  --panel: #f7f8fa;
  /* The Korean faces stay in the list for the one Korean word an English page
     carries, the language link. */
  --sans: "Host Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", sans-serif;
}
:root:lang(ko) {
  --sans: -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Segoe UI", Roboto, sans-serif;
}

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

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

body {
  margin: 0;
  background: #fff;
  color: var(--text);
  font: 400 1.0625rem/1.65 var(--sans);
  overflow-wrap: break-word;
}
:root:lang(ko) body { line-height: 1.75; word-break: keep-all; }

a {
  color: var(--ink);
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}
a:hover { text-decoration-thickness: 2px; }
:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
/* Tabbing down the policy scrolls each link in with room for its ring, which
   a link scrolled flush to the screen edge would lose. */
a { scroll-margin-block: 1.5rem; }

/* Pixel art only. The logo mark is curves and stays antialiased. */
.px { display: block; shape-rendering: crispEdges; }

/* Read by screen readers, not drawn. */
.vh {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The lockup is efaimo-site's nav lockup, measured there (Nav.astro): mark
   19px, gap 0.5rem, wordmark 1.2rem at 660 with -0.037em. align-items:center
   is the optically correct alignment; do not nudge the mark. Here the mark is
   ink, through currentColor, where the dark site sets it white. The padding
   only makes the link a taller tap target. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.375rem 0;
  color: var(--ink);
  text-decoration: none;
}
.brand svg { flex: none; }
.wm {
  font-family: "Host Grotesk", var(--sans);
  font-weight: 660;
  font-size: 1.2rem;
  line-height: 1;
  letter-spacing: -0.037em;
  white-space: nowrap;
}

/* Every page is one column in the same 72rem box: the lockup and the language
   link on the top line, the footer on the bottom line, so moving between pages
   or languages moves neither. The box is at least as tall as the screen, which
   keeps the footer at the bottom and lets the home page centre its picture. */
.page {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100svh;
  max-width: 72rem;
  margin: 0 auto;
  padding: 0 1.25rem;
}
.top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.375rem 0;
}
main { flex: 1 0 auto; }

/* The other language, named in itself (한국어 on English pages, English on
   Korean ones), where a reader looks for it. The padding makes it a 44px-tall
   tap target on the lockup's line. */
.lang {
  padding: 0.625rem 0;
  color: var(--text);
  font-size: 0.9375rem;
  line-height: 1.5rem;
  text-decoration: none;
}
.lang:hover { color: var(--ink); text-decoration: underline; }

/* The home page and the 404: one centred column, picture first, then the
   heading, one muted line and the two ways out, held a little above the middle
   of the space between the top line and the footer. main never shrinks below
   its content, so a short screen only stops the centring; nothing is cut off.
   Each gap is a step on one scale: 28px under the picture, 8px under the
   heading, 20px above the links (40, 12 and 28 on a tablet or wider). */
.hero {
  --cell: 6px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 1.5rem 0 3rem;
  text-align: center;
}

/* The picture is drawn in cells (tools/scene.mjs) and sized here in whole
   pixels per cell. */
.scene { flex: none; }
.sleep { width: calc(28 * var(--cell)); height: calc(23 * var(--cell)); }
.sniff { width: calc(22 * var(--cell)); height: calc(16 * var(--cell)); }

/* Still by default: Mallang lies breathed out with both z a cell up, and the
   motion switch is not shown. A reader gets this picture when they have
   asked for reduced motion, when they turn the switch off, and in a browser
   without :has(), where the switch could not reach the picture. */
.breath { opacity: 0; }
.z { transform: translateY(-1px); }
.motion { display: none; }

/* Motion that runs past five seconds needs a way to stop it (WCAG 2.2.2).
   The page has no script, so the way is a checkbox in the footer with the
   switch role, on by default, and the picture moves only while it is on.
   Mallang breathes: four seconds a breath, the back up for 1.4s of each.
   The two z share the cycle, the big one 1.3s behind, and each rises a cell
   at a time. Every change is a whole cell (steps), the way the app's sprites
   move. */
@supports selector(:has(*)) {
  @media (prefers-reduced-motion: no-preference) {
    .motion { display: inline-block; }
    .page:has(.hold:checked) .breath { animation: breathe 4s steps(1, end) infinite; }
    .page:has(.hold:checked) .z { opacity: 0; transform: none; animation: drift 4s steps(1, end) infinite; }
    .page:has(.hold:checked) .z2 { animation-delay: 1.3s; }
  }
}
@keyframes breathe {
  0% { opacity: 0; }
  55% { opacity: 1; }
  90%, 100% { opacity: 0; }
}
@keyframes drift {
  0% { opacity: 0; transform: translateY(0); }
  10% { opacity: 1; transform: translateY(0); }
  40% { opacity: 1; transform: translateY(-1px); }
  70% { opacity: 1; transform: translateY(-2px); }
  85%, 100% { opacity: 0; transform: translateY(-2px); }
}

.hero h1,
.doc h1 {
  margin: 0;
  color: var(--ink);
  font-size: clamp(1.375rem, 1.1rem + 1.1vw, 1.875rem);
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.015em;
  text-wrap: balance;
}
:root:lang(ko) .hero h1,
:root:lang(ko) .doc h1 { line-height: 1.4; letter-spacing: -0.01em; }
.hero h1 { max-width: 24em; margin-top: 1.75rem; }

/* Centred lines read best when they are even, so balance, not pretty. */
.sub {
  max-width: 30em;
  margin: 0.5rem 0 0;
  color: var(--muted);
  text-wrap: balance;
}

/* The two ways out: one row of two links in the same style, centred as a
   pair, each a 44px-tall tap target. They wrap to two centred lines only
   where one row will not fit. */
.go {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0 1.75rem;
  margin: 1.25rem 0 0;
}
.go a {
  display: inline-flex;
  align-items: center;
  gap: 0.375em;
  padding: 0.625rem 0;
  line-height: 1.5rem;
}
/* Leaves this site. Drawn, because the font subset has no arrow. */
.ext { flex: none; width: 0.625em; height: 0.625em; }

@media (min-width: 48rem) {
  .top { padding: 1.25rem 0; }
}
/* The roomier sizes need a tall window as well as a wide one: a laptop
   browser that shows 657px keeps the phone spacing at the larger cell. */
@media (min-width: 48rem) and (min-height: 40rem) {
  .hero { --cell: 8px; padding: 2.5rem 0 4.5rem; }
  .hero h1 { margin-top: 2.5rem; }
  .sub { margin-top: 0.75rem; }
  .go { margin-top: 1.75rem; }
}

.foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0 1rem;
  padding: 1.25rem 0 1.75rem;
  color: var(--muted);
  font-size: 0.875rem;
  line-height: 1.75;
}
.foot p { margin: 0; }
/* The right-hand group keeps to the right edge even when a narrow screen
   puts it on a line of its own, so the privacy link sits in the same corner
   on every page. */
.foot p:last-child { margin-left: auto; }
/* The privacy link stands apart from the rest of the footer, as Korean
   guidance for privacy policies asks (a different weight or colour). */
.foot .pp { color: var(--ink); font-weight: 500; }
/* A 48px-tall tap target on a 14px line: the padding grows the box and the
   negative margin gives the space back, so the footer does not move. */
.foot a,
.hold-btn { display: inline-block; padding: 0.75rem 0; margin: -0.75rem 0; }

/* The motion switch, on the home page only. The checkbox lies over its
   label, invisible, so a screen reader's focus box lands on the words; the
   label is what a reader sees and presses. Its name is the word before the
   colon. The state words are hidden from assistive technology, which reads
   the switch's own on and off, and they share one grid cell, so turning the
   switch off moves nothing beside it. Underlines do not reach into an inline
   grid, so the state words carry their own. */
.motion { position: relative; margin-right: 1.25rem; }
.hold {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}
.hold-btn {
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.hold-btn,
.st > span {
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}
.hold-btn:hover { color: var(--ink); }
.hold-btn:hover,
.hold-btn:hover .st > span { text-decoration-thickness: 2px; }
.hold:focus-visible + .hold-btn { outline: 2px solid var(--ink); outline-offset: 3px; }
.st { display: inline-grid; }
.st > span { grid-area: 1 / 1; }
.hold:checked + .hold-btn .st-off,
.hold:not(:checked) + .hold-btn .st-on { visibility: hidden; }

/* The privacy policy: a reading column in the middle of the same box, text
   aligned to the left. */
.doc {
  width: 100%;
  max-width: 42rem;
  margin: 0 auto;
  padding: 1.75rem 0 3.5rem;
}
.doc h2 {
  margin: 2.5rem 0 0;
  color: var(--ink);
  font-size: 1.125rem;
  font-weight: 600;
  line-height: 1.5;
}
.doc p,
.doc ul { margin: 0.625rem 0 0; }
.doc ul { padding-left: 1.25rem; }
/* No paragraph ends on one stranded word. Not balance: in a left-aligned
   column that halves the lead's measure. */
.doc p,
.doc li { text-wrap: pretty; }
.lead { color: var(--ink); font-size: 1.125rem; }

.xfer {
  margin: 1rem 0 0;
  padding: 0.875rem 1.125rem 1rem;
  background: var(--panel);
  border: 1px solid var(--rule);
}
.xfer h3 { margin: 0; color: var(--ink); font-size: 1rem; line-height: 1.6; }
.xfer dl {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 0.25rem 1rem;
  margin: 0.5rem 0 0;
  font-size: 0.9375rem;
  line-height: 1.65;
}
.xfer dt { color: var(--muted); }
.xfer dd { margin: 0; }

.stamp { color: var(--muted); }
