/*
 * Diminuendo — "manuscript".
 *
 * Understated on purpose. This is a utility, and the thing that earns trust is
 * the before/after crop on the result screen, not the chrome around it. Every
 * decision here is trying to stay out of the way of that crop.
 *
 * No build step, no framework, no webfonts. The type is a system serif stack
 * for the wordmark and headings and system sans for everything else: PRODUCT.md
 * asked for self-hosted fonts rather than CDN ones, and the stack that ships
 * with the machine is the same idea taken further — nothing to download,
 * nothing to break offline, and no bytes added to a 584 KB payload whose size
 * is part of the pitch.
 *
 * All styling lives here rather than in style attributes, which is what lets
 * _headers drop 'unsafe-inline' from style-src.
 */

:root {
  /* Paper and ink. The accent is the colour of a pencil marking in a score. */
  --paper: #faf7f0;
  --surface: #fffdf8;
  --ink: #1a1a1a;
  --ink-muted: #5f5a52;
  --rule: #ddd6c8;
  --rule-strong: #c4bba8;
  --accent: #a2342a;
  --accent-soft: #f2e4e1;
  --good: #2f6b4f;
  --shadow: 0 1px 2px rgb(26 26 26 / 6%), 0 6px 20px rgb(26 26 26 / 6%);

  --serif: 'Iowan Old Style', 'Palatino Linotype', Palatino, 'Book Antiqua',
           Georgia, 'Times New Roman', serif;
  --sans: system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --measure: 34rem;
  --radius: 6px;
}

/*
 * Dark mode follows the system. There is no in-app toggle, and adding one would
 * mean storing a preference — this app currently writes two keys to
 * localStorage, both of them measurements it needs to avoid crashing, and "no
 * accounts, no cookies" reads better the fewer exceptions it has.
 */
@media (prefers-color-scheme: dark) {
  :root {
    --paper: #171614;
    --surface: #201e1b;
    --ink: #ece7dc;
    --ink-muted: #9d968a;
    --rule: #34302a;
    --rule-strong: #4b463e;
    --accent: #e08c7c;
    --accent-soft: #33231f;
    --good: #7fbf9b;
    --shadow: 0 1px 2px rgb(0 0 0 / 40%), 0 6px 20px rgb(0 0 0 / 30%);
  }
}

* { box-sizing: border-box; }

html { color-scheme: light dark; }

body {
  margin: 0;
  padding: 0;
  background: var(--paper);
  color: var(--ink);
  font: 16px/1.6 var(--sans);
  -webkit-text-size-adjust: 100%;
}

h1, h2, h3 {
  font-family: var(--serif);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
  margin: 0 0 0.5em;
}

h1 { font-size: clamp(1.9rem, 1.4rem + 2.2vw, 2.9rem); }
h2 { font-size: 1.35rem; }
h3 { font-size: 1.05rem; }

p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

a { color: var(--accent); text-underline-offset: 2px; }
a:hover { text-decoration-thickness: 2px; }

/* Everything focusable gets the same visible ring, including the drop zone. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

 /*
  * padding-inline rather than the padding shorthand, so a section can set its
  * own vertical rhythm without either of them clobbering the other. As
  * shorthands they collided invisibly: `.wrap` out-specifies a bare `main`, so
  * `main`'s vertical padding never applied, while `.masthead` came later than
  * `.wrap` and zeroed the gutter instead.
  */
.wrap {
  width: 100%;
  max-width: 46rem;
  margin: 0 auto;
  padding-inline: 1.25rem;
}

/* ---------------------------------------------------------------- masthead */

.masthead {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding-block: 1.5rem 0;
}

/*
 * The mark is icon.svg used as a mask rather than as an <img>, so there is one
 * copy of it — the favicon, the masthead and both themes all read the same
 * file — and so it can take its colour from the theme. An <img> could not:
 * currentColor does not cross into an externally referenced document.
 */
.mark {
  width: 30px;
  height: 30px;
  flex: none;
  background: var(--accent);
  -webkit-mask: url(./icon.svg) center / contain no-repeat;
  mask: url(./icon.svg) center / contain no-repeat;
}

.wordmark {
  font-family: var(--serif);
  font-size: 1.3rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  margin: 0;
}

/*
 * The one branded device: five staff lines as a rule. Says "sheet music"
 * without a clip-art clef, and it is a background image rather than five
 * elements so it can sit on anything.
 */
.stave {
  height: 13px;
  border: 0;
  /* Centred explicitly. This element also carries .wrap, whose `margin: 0 auto`
     a `margin: 2.5rem 0` here would silently override — which is exactly what
     it did, leaving the one branded element on the page hanging off the left. */
  margin: 2.5rem auto;
  background-image: repeating-linear-gradient(
    to bottom,
    var(--rule) 0 1px,
    transparent 1px 3px
  );
  background-size: 100% 13px;
  background-repeat: no-repeat;
}

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

main { padding-block: 2rem; }

.lede {
  font-size: 1.15rem;
  color: var(--ink-muted);
  margin-bottom: 2rem;
}

.screen[hidden] { display: none; }

/* --------------------------------------------------------------- drop zone */

.drop {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  min-height: 15rem;
  padding: 2.5rem 1.5rem;
  text-align: center;
  background: var(--surface);
  border: 2px dashed var(--rule-strong);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color 120ms, background-color 120ms;
}

.drop:hover { border-color: var(--accent); }

/* Set while a file is over the window, so the target is the whole zone rather
   than whatever pixel the pointer happens to be on. */
.drop.is-over {
  border-color: var(--accent);
  border-style: solid;
  background: var(--accent-soft);
}

.drop-title {
  font-family: var(--serif);
  font-size: 1.3rem;
  font-weight: 600;
}

.drop-sub { color: var(--ink-muted); font-size: 0.95rem; }

.drop-privacy {
  margin-top: 1rem;
  font-size: 0.9rem;
  color: var(--ink-muted);
}

.drop-privacy strong { color: var(--ink); font-weight: 600; }

/* The file input is the real control; the drop zone is its label. Hidden
   visually but reachable, so keyboard and screen-reader users get the ordinary
   file picker rather than a div pretending to be one. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------------------------------------------------------------- controls */

.btn {
  font: inherit;
  font-weight: 600;
  padding: 0.65rem 1.25rem;
  min-height: 44px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--accent);
  color: #fff;
  cursor: pointer;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}

.btn:hover { filter: brightness(1.08); }
.btn:disabled { opacity: 0.5; cursor: default; filter: none; }

.btn-quiet {
  background: transparent;
  color: var(--ink);
  border-color: var(--rule-strong);
}

.btn-quiet:hover { border-color: var(--ink-muted); filter: none; }

/*
 * Dark ink on the accent fill, which is light in dark mode. Scoped away from
 * .btn-quiet: that shares the .btn class at equal specificity, so an unscoped
 * rule here won and painted dark text on a dark transparent background —
 * a button with no visible label at all.
 */
@media (prefers-color-scheme: dark) {
  .btn:not(.btn-quiet) { color: #1b1512; }
}

/* ------------------------------------------------------------------ notice */

.notice {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  padding: 1.25rem 1.4rem;
}

.notice h2 { margin-bottom: 0.4rem; }
.notice.is-good { border-left-color: var(--good); }

/* ------------------------------------------------------------------- panel */

.panel {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 1.4rem;
  box-shadow: var(--shadow);
}

.actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin: 1.25rem 0 0;
}

.hint { color: var(--ink-muted); font-size: 0.9rem; margin-top: 1.25rem; }
.hint:empty { display: none; }

/* ----------------------------------------------------------------- working */

.meter {
  height: 10px;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 999px;
  overflow: hidden;
}

.meter-fill {
  height: 100%;
  width: 0;
  background: var(--accent);
  border-radius: 999px;
  transition: width 200ms linear;
}

/*
 * Before the first page comes back there is no proportion to show, so the bar
 * says "something is happening" rather than inventing a number.
 */
.meter-fill.is-indeterminate {
  width: 35%;
  animation: sweep 1.4s ease-in-out infinite;
}

@keyframes sweep {
  0% { margin-left: -35%; }
  100% { margin-left: 100%; }
}

.meter-label {
  margin: 0.75rem 0 0;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------------ result */

.eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--ink-muted);
  margin: 0 0 0.35rem;
}

/* The number, big. It is the whole point of the screen. */
.headline {
  font-size: clamp(2.1rem, 1.3rem + 3.6vw, 3.4rem);
  margin-bottom: 0.4rem;
}

.headline em {
  font-style: normal;
  color: var(--accent);
}

/* ---------------------------------------------------- before/after compare */

.compare {
  margin: 2.5rem 0 0;
  padding: 0;
}

/*
 * Caption on the left, the escape hatch on the right. They share a line because
 * the button is about the caption — which passage this is — rather than about
 * the result as a whole, and putting it under the crops would read as another
 * step in the flow.
 */
.compare-caption {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  color: var(--ink-muted);
  font-size: 0.9rem;
  margin-top: 0.6rem;
}

/*
 * A text button: no fill and no border, because it sits inside a caption and a
 * second filled button next to Download would compete with it. Still 44px of
 * touch target — the padding carries it, and the negative inline margin keeps
 * the label optically aligned with the edge despite that padding.
 */
.btn-inline {
  font: inherit;
  font-weight: 600;
  color: var(--accent);
  background: none;
  border: 0;
  border-radius: 4px;
  padding: 0.6rem 0.4rem;
  margin-inline: -0.4rem;
  min-height: 44px;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: underline;
  text-decoration-color: transparent;
  text-underline-offset: 3px;
  transition: text-decoration-color 120ms;
}

.btn-inline:hover { text-decoration-color: currentColor; }
.btn-inline[disabled] { color: var(--ink-muted); cursor: default; text-decoration-color: transparent; }

.compare-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}

/* One column on a phone: side by side at 100% zoom would show a sliver of each
   and prove nothing. */
@media (max-width: 34rem) {
  .compare-pair { grid-template-columns: 1fr; }
}

.compare-cell { min-width: 0; }

.compare-label {
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-muted);
  margin: 0 0 0.35rem;
}

/*
 * The crop scrolls inside its own box rather than being scaled to fit. Fitting
 * it would resample the pixels and defeat the entire purpose — the claim is
 * that the engraving survived at full resolution, so it has to be shown at full
 * resolution.
 */
.compare-cell canvas {
  display: block;
  width: auto;
  max-width: none;
  background: #fff;
  border: 1px solid var(--rule-strong);
  border-radius: 3px;
  image-rendering: pixelated;
}

.compare-cell {
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

/* -------------------------------------------------------- threshold slider */

.tune {
  margin-top: 2rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 0.9rem 1.2rem;
}

.tune > summary {
  cursor: pointer;
  font-weight: 600;
}

.tune-body { padding-top: 1rem; }

.slider-row {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin: 1rem 0 0.5rem;
}

.slider-end {
  font-size: 0.85rem;
  color: var(--ink-muted);
  white-space: nowrap;
}

@media (max-width: 30rem) {
  .slider-row { flex-wrap: wrap; }
  .slider-row input[type=range] { order: -1; flex-basis: 100%; }
}

/* 44px of touch target, whatever the track looks like. */
input[type=range] {
  flex: 1;
  min-width: 8rem;
  height: 44px;
  margin: 0;
  background: transparent;
  accent-color: var(--accent);
  cursor: pointer;
}

.slider-value {
  font-family: var(--mono);
  font-size: 0.9rem;
  color: var(--ink);
  margin: 0;
  font-variant-numeric: tabular-nums;
}

.slider-help {
  font-size: 0.85rem;
  color: var(--ink-muted);
  margin: 0.5rem 0 0;
}

/* ---------------------------------------------------------------- warnings */

.warn {
  margin-top: 1.5rem;
  padding: 0.9rem 1.1rem;
  background: var(--accent-soft);
  border-radius: var(--radius);
  font-size: 0.92rem;
}

.warn p { margin: 0; }
.warn p + p { margin-top: 0.4rem; }

/* ---------------------------------------------------------------- advanced */

.advanced {
  margin-top: 2.5rem;
  border-top: 1px solid var(--rule);
  padding-top: 1rem;
  font-size: 0.9rem;
}

.advanced > summary {
  cursor: pointer;
  color: var(--ink-muted);
  font-weight: 600;
}

.advanced > summary:hover { color: var(--ink); }

.field-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 1.5rem;
  margin: 1rem 0;
}

.field { display: flex; flex-direction: column; gap: 0.25rem; }
.field > span { color: var(--ink-muted); font-size: 0.85rem; }

.field input, .field select {
  font: inherit;
  padding: 0.4rem 0.5rem;
  min-height: 36px;
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--rule-strong);
  border-radius: 4px;
}

.field input[type=number] { width: 6.5rem; }
.field input[type=text] { width: 9rem; }

.log, .bar {
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.55;
  white-space: pre-wrap;
  overflow-x: auto;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 4px;
  padding: 0.75rem;
  margin: 0.75rem 0 0;
}

.bar { color: var(--accent); }
.bar:empty, .log:empty { display: none; }

.previews { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 0.75rem; }
.previews figure { margin: 0; display: inline-block; text-align: center; }
.previews canvas {
  max-width: 100%;
  border: 1px solid var(--rule-strong);
  background: #fff;
}
.previews figcaption { font-size: 12px; color: var(--ink-muted); }

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

.colophon {
  padding-block: 0 3rem;
  font-size: 0.9rem;
  color: var(--ink-muted);
}

.colophon h2 { font-size: 1.1rem; color: var(--ink); }

.build {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-muted);
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}
