/* ==========================================================================
   modiview.com

   One stylesheet, hand written, no build step. Loaded from page() in
   site/src/layout.js with a ?v= cache buster (ASSET_V) — bump that constant
   whenever this file changes.
   ========================================================================== */


/* --------------------------------------------------------------------------
   0. Metric-matched fallback
   Oversized headlines swap fonts very visibly. Overriding the system face to
   Pretendard's own vertical metrics (unitsPerEm 1000, hhea ascender 928,
   descender -232) keeps the line box identical across the swap, so the
   headline does not reflow when the webfont lands.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: 'Pretendard Fallback';
  src: local('Apple SD Gothic Neo'), local('AppleSDGothicNeo-Regular'), local('Malgun Gothic'), local('맑은 고딕');
  ascent-override: 92.8%;
  descent-override: 23.2%;
  line-gap-override: 0%;
}


/* --------------------------------------------------------------------------
   1. Tokens
   Two type registers carry the whole design: prose in the sans, every
   technical fact in the mono. Sand is an annotation colour — it never fills
   a shape.
   -------------------------------------------------------------------------- */
:root {
  color-scheme: light;
  --paper: #FBFAF8;
  --ink: #14120F;
  --graphite: #6B6660;
  --sand: #B98C61;
  --rule: #E4DED4;
  --live: #2F6F4F;
  --sans: -apple-system, BlinkMacSystemFont, 'Apple SD Gothic Neo', 'Malgun Gothic', 'Helvetica Neue', Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, 'Consolas', monospace;
  /* Pretendard is scoped to the display register only. Applied to body text it
     would pull 10-15 unicode-range subsets instead of the 6 the headlines need,
     which is the difference between ~157KB and half a megabyte. Its Latin is
     Inter-derived, so no second Latin display face is needed - pairing a
     geometric Latin at 900 with Hangul at 850 mismatches exactly where the
     type is largest. */
  --display: 'Pretendard Variable', 'Pretendard Fallback', var(--sans);
  --gutter: clamp(20px, 5vw, 72px);
  /* Single source of truth for the sticky header height — the split section
     offsets against it. Overridden in the 640px block below. */
  --header-h: 62px;
}


/* --------------------------------------------------------------------------
   2. Base
   -------------------------------------------------------------------------- */
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
/* No image may be wider than the box it is in. There was no rule for this at
   all: every image on the site was sized by whichever section rule happened to
   match, so one missing or stale rule put a 1,600px photograph on a 1,440px
   screen and pushed the whole layout sideways. The sections that place their
   own images set width and height explicitly and win on specificity. */
img { max-width: 100%; height: auto; }

body {
  margin: 0; background: var(--paper); color: var(--ink);
  font-family: var(--sans); font-size: 16px; line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  /* Korean must break between 어절, never inside one. */
  word-break: keep-all;
  overflow-wrap: break-word;
}
a { color: inherit; }
:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }


/* --------------------------------------------------------------------------
   3. Type
   -------------------------------------------------------------------------- */
/* Every label, status and number is monospaced — that is the spec-sheet voice. */
.mono { font-family: var(--mono); font-size: 12px; letter-spacing: .08em; text-transform: uppercase; }
.eyebrow { font-family: var(--mono); font-size: 11px; letter-spacing: .18em; text-transform: uppercase; color: var(--sand); margin: 0 0 18px; }

h1, h2, h3, .brand { font-family: var(--display); }
h1 { font-size: clamp(34px, 6.4vw, 64px); line-height: 1.06; letter-spacing: -.035em; font-weight: 800; margin: 0 0 20px; }
h2 { font-size: clamp(22px, 3vw, 30px); line-height: 1.2; letter-spacing: -.025em; font-weight: 700; margin: 0 0 14px; }
h3 { font-size: 17px; margin: 0 0 8px; letter-spacing: -.01em; }
p { margin: 0 0 14px; }
.lede { font-size: clamp(16px, 2vw, 19px); color: var(--graphite); max-width: 52ch; }

/* The oversized register. text-wrap: balance keeps the ragged edge readable
   at the sizes this runs at. */
.display {
  font-family: var(--display); font-weight: 800;
  font-size: clamp(38px, 7.2vw, 104px);
  line-height: 1.0; letter-spacing: -.042em;
  text-wrap: balance; margin: 0;
}
.display--sm { font-size: clamp(30px, 4.4vw, 60px); letter-spacing: -.035em; }
.statement { font-size: clamp(18px, 2.6vw, 30px); line-height: 1.35; letter-spacing: -.02em; max-width: 32ch; color: var(--graphite); margin: 0; }
/* Display type sets on a 1.0 line box, so the following line needs the air put
   back by hand. */
.display + .statement, .display + .tags, .display + .work__line { margin-top: clamp(28px, 4vw, 56px); }
.eyebrow + .display { margin-top: 0; }

/* A record row: date in mono, the fact in prose. */
.log { list-style: none; margin: 0; padding: 0; }
.log li { display: grid; gap: 4px 28px; padding: 20px 0; border-top: 1px solid var(--rule); }
.log li:last-child { border-bottom: 1px solid var(--rule); }
.log time { font-family: var(--mono); font-size: 12px; letter-spacing: .08em; color: var(--sand); }
.log p { margin: 0; max-width: 60ch; }
@media (min-width: 720px) {
  .log li { grid-template-columns: 110px 1fr; }
}

/* Tag pills, straight off the reference. */
.tags { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; margin: 22px 0 0; padding: 0; }
.tags li { font-family: var(--mono); font-size: 11px; letter-spacing: .08em; text-transform: uppercase; border: 1px solid var(--rule); border-radius: 2em; padding: 7px 14px; color: var(--graphite); }


/* --------------------------------------------------------------------------
   4. Layout
   Sections are full-bleed blocks that own their own vertical rhythm. They are
   deliberately NOT separated by an automatic hairline - that rule is what kept
   the old page a single column of same-coloured bands. Rules are now placed on
   purpose with .rule, and a section can carry its own ground with
   .section--dark.
   -------------------------------------------------------------------------- */
section { padding-block: clamp(56px, 9vw, 128px); }
/* Continuation of the block above it - the full rhythm would read as a gap. */
.section--tight { padding-block: 0 clamp(48px, 7vw, 96px); }

/* Flipping the four ground tokens is the whole dark treatment. Every rule in
   this file routes colour through them, so .btn, .rule, .eyebrow and the type
   all invert without a single per-element override. */
.section--dark {
  --paper: #0A0A0A;
  --ink: #F5F3EF;
  --rule: #2A2724;
  --graphite: #9A948C;
  /* #B98C61 fails contrast on near-black; lift it just for this ground. */
  --sand: #D8A87A;
  background: var(--paper);
  color: var(--ink);
}

.wrap { max-width: 1440px; margin: 0 auto; padding-inline: var(--gutter); }
.wrap--narrow { max-width: 720px; }
/* Sections share one left edge; prose is constrained per element (a ch
   measure on .statement, .log p, .facts dd) rather than by centring a
   narrower wrapper, which would shift the alignment section to section. */

.rule { border: 0; border-top: 1px solid var(--rule); margin: 0; }
.stack > * + * { margin-top: var(--stack-gap, 16px); }


/* --------------------------------------------------------------------------
   4b. The sticky split
   Left column holds the text and pins; right column scrolls its images past
   it. Mobile-first: the base state is a single stacked column, and sticky is
   the enhancement. If the media query never matches the section still reads as
   a normal list of image-and-caption blocks.
   -------------------------------------------------------------------------- */
.split { display: grid; gap: 40px; }
.split + .split { margin-top: clamp(72px, 10vw, 160px); }
.split__flow { display: grid; gap: 20px; min-width: 0; }
.split__flow img { display: block; width: 100%; height: auto; }

@media (min-width: 900px) {
  .split {
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    gap: clamp(48px, 6vw, 120px);
    /* Required. The default `stretch` makes the grid item full height, which
       leaves nothing to scroll past and sticky silently does nothing. */
    align-items: start;
  }
  .split__sticky {
    position: sticky;
    top: calc(var(--header-h) + 48px);
    align-self: start;
    /* Guard: a sticky column taller than the viewport scrolls instead of
       pinning. Keep the contents to eyebrow + name + one line + one link. */
    max-height: calc(100vh - var(--header-h) - 96px);
  }
}

/* WARNING: any ancestor with overflow:hidden kills position:sticky silently.
   If a section ever needs to clip a child, use clip-path: inset(0) on that
   child - never overflow on an ancestor of .split. */


/* --------------------------------------------------------------------------
   5. Components
   -------------------------------------------------------------------------- */
header.bar { border-bottom: 1px solid var(--rule); position: sticky; top: 0; background: var(--paper); z-index: 10; }
.bar .wrap { display: flex; align-items: center; justify-content: space-between; height: var(--header-h); gap: 24px; }
.brand { display: flex; align-items: center; gap: 10px; text-decoration: none; font-weight: 700; letter-spacing: -.02em; }
.brand img { width: 24px; height: 24px; display: block; }
.bar nav { display: flex; gap: 26px; }
.bar nav a { font-family: var(--mono); font-size: 12px; letter-spacing: .1em; text-transform: uppercase; text-decoration: none; color: var(--graphite); }
.bar nav a:hover, .bar nav a[aria-current] { color: var(--ink); }

.btn { display: inline-flex; align-items: center; gap: 8px; font-family: var(--mono); font-size: 12px; letter-spacing: .1em; text-transform: uppercase; text-decoration: none; padding: 13px 20px; border: 1px solid var(--ink); background: var(--ink); color: var(--paper); }
.btn:hover { background: transparent; color: var(--ink); }
.btn.ghost { background: transparent; color: var(--ink); }
.btn.ghost:hover { background: var(--ink); color: var(--paper); }



/* --------------------------------------------------------------------------
   6. Utilities
   These replace the inline style="" attributes the old templates carried, so
   the CSP can drop 'unsafe-inline' from style-src.
   -------------------------------------------------------------------------- */
.pad-t-sm { margin-top: 18px; }
.pad-t-md { margin-top: 22px; }
.pad-t-lg { margin-top: 28px; }
.pad-t-xl { margin-top: clamp(48px, 7vw, 96px); }
.flush { margin: 0; }
.ink { color: var(--ink); }
.muted { color: var(--graphite); }


/* --------------------------------------------------------------------------
   6a. Definition rows — principles, product facts, business details
   -------------------------------------------------------------------------- */
.facts { margin: 0; }
.facts > div { display: grid; gap: 6px 32px; padding: 22px 0; border-bottom: 1px solid var(--rule); }
.facts dt { font-weight: 700; letter-spacing: -.01em; }
.facts dd { margin: 0; color: var(--graphite); max-width: 52ch; }
@media (min-width: 720px) {
  .facts > div { grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); align-items: baseline; }
}


/* --------------------------------------------------------------------------
   6b. Page module — featured work
   -------------------------------------------------------------------------- */
.work__line { font-size: clamp(15px, 1.6vw, 18px); line-height: 1.5; color: var(--graphite); max-width: 34ch; margin: 14px 0 0; }
.work__status { font-size: 14px; line-height: 1.6; color: var(--graphite); max-width: 38ch; margin: 22px 0 0; padding-top: 20px; border-top: 1px solid var(--rule); }
.work__links { display: flex; flex-wrap: wrap; gap: 10px 20px; margin: 20px 0 0; }
.work__links a { font-family: var(--mono); font-size: 12px; letter-spacing: .08em; text-transform: uppercase; }
/* The Spot screenshots are white-ground UI; a frame keeps them from floating
   on the dark section. */
.work__flow--framed img { border: 1px solid var(--rule); }


/* --------------------------------------------------------------------------
   7. Page module — fit matrix
   The matrix is the signature of the site. A merchant's first question is
   "does this fit my platform", so the answer leads — including the gaps.
   -------------------------------------------------------------------------- */
.matrix { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 12px; }
.matrix th, .matrix td { border-bottom: 1px solid var(--rule); padding: 14px 10px; text-align: left; vertical-align: baseline; }
.matrix thead th { font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--graphite); font-weight: 400; border-bottom-color: var(--ink); }
.matrix td:first-child, .matrix th:first-child { padding-left: 0; }
.matrix .name { font-family: var(--sans); font-size: 16px; font-weight: 700; letter-spacing: -.01em; }
.matrix .name span { display: block; font-family: var(--mono); font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--sand); font-weight: 400; margin-bottom: 3px; }
.matrix .price { white-space: nowrap; color: var(--graphite); }
.cell { display: inline-block; }
.cell.live { color: var(--live); }
.cell.wait { color: var(--ink); }
.cell.none { color: var(--rule); }
.matrix tbody tr:hover { background: #F4F1EC; }
.note { font-family: var(--mono); font-size: 11px; letter-spacing: .06em; color: var(--graphite); margin-top: 16px; }


/* --------------------------------------------------------------------------
   8. Page module — product cards
   -------------------------------------------------------------------------- */
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1px; background: var(--rule); border: 1px solid var(--rule); }
.card { background: var(--paper); padding: 28px 24px 24px; }
.card .spec { list-style: none; margin: 12px 0; padding: 0; font-family: var(--mono); font-size: 12px; color: var(--graphite); }
.card .spec li::before { content: "· "; }


/* --------------------------------------------------------------------------
   9. Page module — order form
   -------------------------------------------------------------------------- */
.form { max-width: 640px; }
.field { margin-bottom: 26px; }
.field > label, .field > .label { display: block; font-family: var(--mono); font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--graphite); margin-bottom: 8px; }
.field .req { color: var(--sand); }
.field .hint { font-size: 13px; color: var(--graphite); margin: 6px 0 0; }
input[type=text], input[type=url], textarea {
  width: 100%; padding: 12px 14px; border: 1px solid var(--rule); background: #fff;
  font-family: var(--sans); font-size: 15px; color: var(--ink); border-radius: 0;
}
input:focus, textarea:focus { outline: none; border-color: var(--ink); }
textarea { min-height: 190px; resize: vertical; line-height: 1.6; }
.choices { display: flex; flex-wrap: wrap; gap: 8px; }
.choices label { font-family: var(--mono); font-size: 12px; letter-spacing: .06em; border: 1px solid var(--rule); padding: 10px 14px; cursor: pointer; background: #fff; }
.choices input { position: absolute; opacity: 0; width: 0; height: 0; }
.choices input:checked + span { color: var(--paper); }
.choices label:has(input:checked) { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.choices label:has(input:focus-visible) { outline: 2px solid var(--ink); outline-offset: 3px; }
.consent { display: flex; gap: 10px; align-items: flex-start; font-size: 14px; }
.consent input { margin-top: 4px; }
.err { border: 1px solid var(--sand); background: #FBF3EA; padding: 14px 16px; margin-bottom: 26px; font-size: 14px; }
.field.bad input, .field.bad textarea { border-color: var(--sand); }
.field .msg { font-size: 13px; color: #A3542B; margin: 6px 0 0; }
.receipt { border: 1px solid var(--ink); padding: 28px 26px; max-width: 560px; }
.receipt .no { font-family: var(--mono); font-size: 12px; letter-spacing: .14em; color: var(--sand); }


/* --------------------------------------------------------------------------
   10. Page module — legal documents
   -------------------------------------------------------------------------- */
.doc { max-width: 720px; }
.doc h2 { font-size: 18px; margin: 34px 0 10px; padding-top: 24px; border-top: 1px solid var(--rule); }
.doc h2:first-of-type { border-top: 0; padding-top: 0; margin-top: 0; }
.doc p, .doc li { font-size: 15px; color: #2A2621; }
.doc ul { padding-left: 18px; }
/* The legal tables are the only tables on the site and they carry long unbroken
   strings - an address, a registration number. At 320 a two-column row could not
   fit either cell and pushed 3px past the viewport, which is a horizontal
   scrollbar on the narrowest phones. table-layout: fixed makes the columns obey
   the table's width, and the wrap rule lets the values break. */
.doc table { width: 100%; table-layout: fixed; border-collapse: collapse; font-size: 14px; margin: 12px 0 18px; }
.doc table :is(th, td) { overflow-wrap: anywhere; word-break: keep-all; }
.doc th, .doc td { border: 1px solid var(--rule); padding: 10px 12px; text-align: left; vertical-align: top; }
.doc th { background: #F4F1EC; font-weight: 600; white-space: nowrap; }
.updated { font-family: var(--mono); font-size: 12px; letter-spacing: .08em; color: var(--graphite); margin-bottom: 32px; }


/* --------------------------------------------------------------------------
   11. Breakpoints
   -------------------------------------------------------------------------- */
@media (max-width: 720px) {
  .matrix thead { display: none; }
  .matrix tr { display: block; border-bottom: 1px solid var(--rule); padding: 16px 0; }
  .matrix td { display: inline-block; border: 0; padding: 2px 14px 2px 0; }
  .matrix td:first-child { display: block; margin-bottom: 6px; }
  .matrix td[data-p]::before { content: attr(data-p) " "; color: var(--graphite); }
}

@media (max-width: 640px) {
  :root { --header-h: 56px; }
  .bar nav { gap: 16px; }
}


/* --------------------------------------------------------------------------
   12. Motion
   The default state is VISIBLE. The hidden state only exists under .has-motion,
   which motion.js adds to <html> after checking prefers-reduced-motion. So if
   JS is blocked, or the CSP is misconfigured, or the visitor asked for reduced
   motion, nothing is ever left stuck at opacity 0.
   -------------------------------------------------------------------------- */

.has-motion .reveal,
.has-motion .reveal-group > * {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity .7s cubic-bezier(.16, 1, .3, 1), transform .7s cubic-bezier(.16, 1, .3, 1);
}
.has-motion .reveal.is-in,
.has-motion .reveal-group.is-in > * { opacity: 1; transform: none; }

/* Staggered children. Eight steps is enough for every group on the site. */
.has-motion .reveal-group > *:nth-child(2) { transition-delay: 70ms; }
.has-motion .reveal-group > *:nth-child(3) { transition-delay: 140ms; }
.has-motion .reveal-group > *:nth-child(4) { transition-delay: 210ms; }
.has-motion .reveal-group > *:nth-child(5) { transition-delay: 280ms; }
.has-motion .reveal-group > *:nth-child(6) { transition-delay: 350ms; }
.has-motion .reveal-group > *:nth-child(7) { transition-delay: 420ms; }
.has-motion .reveal-group > *:nth-child(8) { transition-delay: 490ms; }

/* Screenshots wipe in. Same observer, no extra JS. */
.has-motion .split__flow.reveal img { clip-path: inset(0 0 100% 0); transition: clip-path .9s cubic-bezier(.16, 1, .3, 1); }
.has-motion .split__flow.reveal.is-in img { clip-path: inset(0); }


/* --------------------------------------------------------------------------
   14. Reduced motion — stays last, stays authoritative
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; scroll-behavior: auto !important; }
  .reveal, .reveal-group > *, .split__flow img {
    opacity: 1 !important; transform: none !important; clip-path: none !important;
  }
}
