/*
 * ui.css: the house UI.
 *
 * This is not the creator's widget. That one is composed from six axes, shipped
 * over /api/themes, and painted into a shadow root from /assets/creator-page.css
 * and tip.js. This sheet is the other thing: our own pages, which have one
 * committed look and no axes at all.
 *
 * It exists because five pages were carrying their own copy of a cream, serif,
 * green world while the homepage carried the coffee world. Between them that was
 * three token vocabularies, 39 spacing values and 32 font sizes. Here there are
 * one vocabulary and 7 spacings and 6 sizes.
 *
 * The world is the homepage's, documented in DESIGN.md: matte bag stock and
 * chalk white grounds, near-black ink, one cobalt that names the active state,
 * wide-tracked caps as labels, hairline rules that behave like printed ones.
 *
 * Three things are deliberately absent, and each was present before:
 *   - no kicker or eyebrow above a heading
 *   - no coloured border-left on a card or a note
 *   - no card boxes. Sections are hairline-separated bands and their content is
 *     data rows, a label column and a value column
 */

/* ------------------------------------------------------------------ tokens */
:root {
  /* Grounds. Kraft appears only on --panel, never as the page. */
  --paper: #f8f8f5;
  --panel: #eceae3;
  --panel-2: #e2dfd6;

  /* Ink. Contrast on --paper, measured: ink 17.7:1, ink-70 6.8:1,
     ink-label 5.9:1, accent 7.9:1, danger 6.9:1. All clear AA. */
  --ink: #101211;
  --ink-70: rgba(16, 18, 17, .7);
  --ink-label: rgba(16, 18, 17, .66);
  --ink-36: rgba(16, 18, 17, .36);

  --rule: rgba(16, 18, 17, .16);
  --rule-hard: rgba(16, 18, 17, .34);

  /* One accent, and it means the active state. Danger is the only other colour
     and it exists to name a failure, which is a different axis from emphasis. */
  --accent: #1b3cc4;
  --on-accent: #ffffff;
  --danger: #a3242b;

  /* Plus Jakarta Sans runs the interface and Young Serif runs the wordmark and
     every heading. Young Serif ships a single weight, so a heading is 400 by
     definition; the face carries the emphasis and a synthetic bold would only
     smear it. */
  --sans: "Plus Jakarta Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --display: "Young Serif", Georgia, "Times New Roman", serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* Seven steps. Everything sits on one of them. */
  --s1: 2px;
  --s2: 4px;
  --s3: 8px;
  --s4: 12px;
  --s5: 16px;
  --s6: 24px;
  --s7: 32px;

  /* Six steps, and 11px is the floor for anything a person reads. The heading
     steps are a notch larger than they were under a sans: a serif reads smaller
     at the same pixel size. */
  --t-display: clamp(1.75rem, 1.35rem + 1.6vw, 2.5rem);
  --t-h2: 1.25rem;
  --t-h3: 1rem;
  --t-body: 1rem;
  --t-small: .875rem;
  --t-micro: .6875rem;

  --r-1: 2px;
  --r-pill: 999px;

  --dur: .16s;
  --ease: cubic-bezier(.16, 1, .3, 1);

  --pad: clamp(20px, 4vw, 56px);
  --measure: 68ch;
}

/* ------------------------------------------------------------------- reset */
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--t-body);
  line-height: 1.55;
  /* Every number on these pages is money, a count or a date, and all of them
     align by column rather than by luck. */
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
}

/* The browser surfaces nobody draws, themed from the same palette. Cheap, and
   the clearest signal that a page was built rather than assembled. */
::selection { background: var(--accent); color: var(--on-accent); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-1); }
input, button, textarea, select { font: inherit; color: inherit; caret-color: var(--ink); }

/*
 * Scrollbars. The last browser surface nobody draws, and the one that most
 * reliably belongs to no design system: a stock scrollbar beside a hairline rule
 * and an ink palette is the loudest small thing on a page that is otherwise
 * built. Thin, square, and drawn from the same rule colour.
 *
 * Firefox takes the two properties; WebKit and Blink take the pseudo-elements.
 * Both are set, because either browser alone leaves half the visitors with the
 * default.
 */
html { scrollbar-width: thin; scrollbar-color: var(--rule-hard) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--rule-hard); border-radius: 0; }
::-webkit-scrollbar-thumb:hover { background: var(--rule); }

/* The headings are the display face at its only weight. Tracking is looser than
   the sans needed, because a serif closes its own counters when it is tightened. */
h1, h2, h3 {
  margin: 0; font-family: var(--display); font-weight: 400;
  letter-spacing: -.01em; line-height: 1.1;
}
h1 { font-size: var(--t-display); line-height: 1.06; }
h2 { font-size: var(--t-h2); line-height: 1.2; }
h3 { font-size: var(--t-h3); line-height: 1.25; }
p { margin: 0; }
a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--ink); }

/* ------------------------------------------------------------------ layout */
/* 1240px is the homepage's measure and stays the default. Form and reading
   pages narrow themselves; a 1240px form is a form nobody finishes. */
.wrap { max-width: 1240px; margin: 0 auto; padding: 0 var(--pad); }
.wrap.narrow { max-width: 44rem; }
/* Form and reading pages carry their own vertical rhythm. The homepage does not
   use this, because its stage sits directly under the header by design. */
.page { padding-block: var(--s7); }
.lede { margin-top: var(--s4); max-width: var(--measure); color: var(--ink-70); }

/* ------------------------------------------------------------------ chrome */
header { border-bottom: 1px solid var(--rule); }
.bar { display: flex; align-items: center; gap: var(--s5); min-height: 58px; }
/*
 * The wordmark: the cup, then the name, in the display face.
 *
 * The image is the silhouette in a single ink. The source illustration carries
 * six colours and a soft 3D finish, which turns to mud below roughly 48px; this
 * is drawn at 24. Source art is at the repo root as belanja-kopi-logo-2.svg and
 * the derivation is noted in the SVG itself.
 */
.mark {
  display: inline-flex; align-items: center; gap: var(--s3);
  font-family: var(--display); font-weight: 400;
  font-size: var(--t-h3); letter-spacing: -.01em;
  text-decoration: none;
}
.mark img { display: block; width: 24px; height: 24px; flex: none; }
/* The one action a header carries, and it sits at the far end. A button here
   would compete with the page's own submit, so it stays a link. */
.bar-link {
  margin-left: auto;
  font-size: var(--t-small); color: var(--ink-70);
  text-decoration: underline; text-underline-offset: 3px;
}
.bar-link:hover { color: var(--ink); }

footer { border-top: 1px solid var(--rule); padding: var(--s6) 0 40px; margin-top: var(--s3); }
.foot-links {
  display: flex; flex-wrap: wrap; gap: 14px var(--s6); align-items: baseline;
  font-size: .8125rem; color: var(--ink-label);
}
.foot-links a { color: var(--ink-70); }

/* A tracked micro-label. It carries a label, never an eyebrow over a heading:
   on these pages it sits at the right end of a band header, opposite the name,
   the way a lot code sits opposite a currency on a bag. */
.micro {
  font-size: var(--t-micro);
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-label);
}

/* --------------------------------------------------------------- controls */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s3);
  border: 1px solid var(--ink); background: var(--ink); color: var(--paper);
  padding: var(--s4) 18px; border-radius: var(--r-1); cursor: pointer;
  font-size: .8125rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  /* Without this an <a class="btn"> keeps the user-agent underline, because the
     `a` rule above sets the thickness and the offset but not the line itself. */
  text-decoration: none;
  min-height: 44px;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}
.btn:hover { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn.ghost { background: transparent; color: var(--ink); }
.btn.ghost:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }
/* Smaller only in its padding. A shorter label is not a smaller target. */
.btn.sm { padding: var(--s4) 14px; }
.btn[disabled] { opacity: .5; cursor: default; }
.btn[disabled]:hover { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.btn.ghost[disabled]:hover { background: transparent; color: var(--ink); border-color: var(--ink); }

/* ---------------------------------------------------------------- the form */
.field { display: grid; gap: var(--s2); }
.field + .field { margin-top: var(--s5); }
.field label { font-size: var(--t-small); font-weight: 700; }
.field .hint {
  display: block; margin-top: var(--s1);
  font-size: var(--t-small); font-weight: 400; color: var(--ink-label);
  max-width: var(--measure);
}

input[type="text"], input[type="email"], input[type="tel"], input[type="url"],
input[type="password"], input[type="number"], select, textarea {
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule-hard);
  border-radius: var(--r-1);
  appearance: none;
}
select {
  /* The native arrow is the one glyph the palette cannot reach, so the space for
     it is reserved and the control keeps its own. */
  padding-right: var(--s7);
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-70) 50%),
                    linear-gradient(135deg, var(--ink-70) 50%, transparent 50%);
  background-position: right 18px center, right 13px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
textarea { min-height: 6rem; resize: vertical; line-height: 1.5; }
input::placeholder, textarea::placeholder { color: var(--ink-label); opacity: 1; }
input:focus, select:focus, textarea:focus { border-color: var(--accent); }
input[readonly] { color: var(--ink-70); }

/* A prefix that belongs to the field, so it reads as one control. */
.row-input { display: flex; align-items: baseline; gap: var(--s3); }
.row-input input { flex: 1; min-width: 0; width: auto; }
.row-input .prefix { font-family: var(--mono); font-size: var(--t-small); color: var(--ink-label); white-space: nowrap; }

.check { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--s3); align-items: start; }
.check input { width: 24px; height: 24px; min-height: 0; margin: 6px 0 0; accent-color: var(--accent); flex: none; cursor: pointer; }
.check label { font-size: var(--t-small); font-weight: 400; padding: 8px 0; cursor: pointer; }

.actions { display: flex; flex-wrap: wrap; gap: var(--s4); align-items: center; margin-top: var(--s6); }
/* Fields that live in a control cluster keep the row instead of stacking, so a
   queue row can carry its inputs and its buttons on one line. */
.actions input[type="text"], .actions input[type="number"], .actions select { width: auto; min-width: 8rem; }
.actions .status { margin: 0; }

/* ------------------------------------------------------------------ bands */
/* The one container. A printed band: a hairline across the top, the name and
   the data at opposite ends, then a hairline, then the content. No box, no
   radius, no shadow, and no colour on one edge.

   The top margin is not reset on a first child. A band that follows a lede still
   needs air above its rule, and the craft floor asks for more space above a
   heading than below it. */
.band { border-top: 1px solid var(--rule-hard); padding-top: var(--s4); margin-top: var(--s7); }
.band-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s4); padding-bottom: var(--s3);
  border-bottom: 1px solid var(--rule); margin-bottom: var(--s5);
}
.band-note { margin: 0 0 var(--s5); font-size: var(--t-small); color: var(--ink-label); max-width: var(--measure); }
.band .btn { margin-top: var(--s5); }

/* ------------------------------------------------------------- data rows */
/* DESIGN.md: "Components are data rows: a label column and a value column."
 *
 * A two-column grid rather than a row wrapper, so a dt/dd pair can be emitted
 * conditionally without a second element to keep them together. */
.rows {
  display: grid;
  grid-template-columns: minmax(6rem, 11rem) minmax(0, 1fr);
  border-top: 1px solid var(--rule);
  margin: 0;
}
.rows dt {
  padding: var(--s3) var(--s4) var(--s3) 0;
  border-bottom: 1px solid var(--rule);
  font-size: var(--t-micro); font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink-label);
}
.rows dd {
  margin: 0; padding: var(--s3) 0;
  border-bottom: 1px solid var(--rule);
  font-size: var(--t-small); overflow-wrap: anywhere;
}
.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
code { font-family: var(--mono); font-size: .9em; background: var(--panel); padding: 1px 5px; border-radius: var(--r-1); }

/* An aside on printed rules, top and bottom. Not a callout with a coloured edge. */
.note {
  margin: var(--s5) 0 0;
  padding: var(--s4) 0;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  font-size: var(--t-small);
  color: var(--ink-70);
  max-width: var(--measure);
}
.note.bad { color: var(--danger); }

/* ----------------------------------------------------------------- status */
.status { margin: var(--s4) 0 0; min-height: 1.5em; font-size: var(--t-small); color: var(--ink-70); }
.status.ok { color: var(--ink); font-weight: 700; }
.status.bad { color: var(--danger); font-weight: 700; }

.empty { padding: var(--s6) 0; font-size: var(--t-small); color: var(--ink-label); }

/* A state, stamped rather than pilled: squared, hairline, small caps. */
.tag {
  display: inline-block; margin-left: var(--s3); padding: 1px var(--s3);
  border: 1px solid var(--rule-hard); border-radius: var(--r-1);
  font-size: var(--t-micro); font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-label); vertical-align: 2px;
}
.tag[data-state="live"] { border-color: var(--ink); color: var(--ink); }
.tag[data-state="rejected"] { border-color: var(--danger); color: var(--danger); }

/* The honeypot. Present in the DOM for a bot, out of the tab order and the
   accessibility tree for everybody else. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/*
 * The rotating trade in the headline.
 *
 * A fade alone reads as a blink: the old word is gone and the new one appears in
 * the same place, and the eye catches the swap. Fading through a short blur is what
 * reads as dissolving, because the word loses its edges before it loses its place.
 * Blur and transform are both compositor properties, so this costs no layout.
 *
 * The heading holds one line, and that is a sizing problem rather than a styling
 * one. "Built for" plus the longest trade is about twenty-five characters, which
 * needs roughly 390px at the display size on a phone. A 320px screen has about
 * 280px, so the heading is stepped down on very narrow screens to keep the line
 * whole. Measured at 320, 390 and 768.
 *
 * Motion stops for anybody who has asked motion to stop. The word still changes,
 * because that is content, and only the dissolve is dropped, which is a rule this
 * codebase already follows in src/creator/sheet.css.
 */

/* Narrow screens first: this is the width where the line would otherwise break. */
@media (max-width: 22.5rem) {
  main > h1 { font-size: 1.375rem; }
}

#rotator {
  display: inline-block;
  /* One duration for the dissolve, read by the script so the text swaps at the
     moment the word is least visible instead of at a second guess. The two
     durations differ on purpose: leaving is decisive, arriving is unhurried. */
  --dissolve: 170ms;
  --arrive: 340ms;
  transition: opacity var(--arrive) var(--ease), filter var(--arrive) var(--ease),
              transform var(--arrive) var(--ease);
}
#rotator[data-out="1"] {
  opacity: 0;
  filter: blur(6px);
  transform: translateY(-.07em);
  transition-duration: var(--dissolve);
  transition-timing-function: cubic-bezier(.5, 0, .9, .4);
}
/* A wider blur on a heading that is this large, and gone by the time it lands. */
@media (min-width: 40rem) { #rotator[data-out="1"] { filter: blur(7px); } }

@media (prefers-reduced-motion: reduce) {
  #rotator { transition: none; }
}
