/* ============================================================================
   Backstitch — neumorphic design system, applied to the Signature Stitch flow.
   Mobile-first. Values reproduced from design/README.md (hifi spec):
   light is ALWAYS top-left; raised = actionable, inset = holds state.

   READABILITY PASS (this build). Audience: quilters, often older, frequently
   low-vision, often on an iPad. The design README says it itself —
   "neumorphism is low-contrast by nature" — and several of its text tokens
   measure below WCAG AA. Where beauty and legibility conflicted, legibility
   won. Every change is proved by app/contrast.spike.mjs, which parses THIS
   file, resolves each rule's colour + size against the surface it sits on, and
   fails the build if any readable pairing drops under 4.5:1.
   ========================================================================= */

/* ---- TOKENS (mobile base) ------------------------------------------------ */
:root {
  /* surface & neutrals — UNCHANGED from the design system */
  --bg:   #E9E2D8;   /* page background (warm linen) */
  --srf:  #ECE5DB;   /* card / control surface       */
  --srf2: #E4DCD1;   /* recessed surface, mats       */
  --txt:  #453C31;   /* primary text    — 7.95:1 on --srf2, passes */
  --body: #5D5348;   /* body paragraph  — 5.53:1 on --srf2, passes */

  /* RETIRED FROM TEXT. Kept so the design-system lineage is legible, but no
     rule in this file may set `color:` to either — contrast.spike.mjs asserts
     that directly. Measured against --srf2 (the darkest surface text sits on):
       --mut #8D8172 = 2.80:1  FAIL
       --fnt #A99C8B = 1.98:1  FAIL (badly) */
  --mut:  #8D8172;   /* decorative / ornament only */
  --fnt:  #A99C8B;   /* decorative / ornament only */

  /* READABLE REPLACEMENTS — darkened text variants, so the surface palette
     stays exactly as designed and only the ink gets heavier.
       --mut-text  #655C4E on --srf2 = 4.84:1  (was --mut, 2.80:1)
       --acc-text  #7F4F1E on --srf2 = 5.09:1  (--acc is 2.64:1; the ticket's
                   suggested --accd #8F5A27 measures 4.22:1 on --srf2 and does
                   NOT clear 4.5, so accent TEXT gets its own darker token)
       --done-text #4F6146 on --srf2 = 4.94:1  (was --done, 3.80:1) */
  --mut-text:  #655C4E;
  --acc-text:  #7F4F1E;
  --done-text: #4F6146;

  /* semantic */
  --acc:  #B9793A;   /* single UI accent (ochre) — SURFACES/ORNAMENT, not text */
  --accd: #8F5A27;   /* accent pressed / gradient end */
  /* Accent gradient darkened. The old ends (#C98A4A -> #A96A2C) put the
     on-accent label at 2.72:1 on the light end — below even the 3:1 large-text
     floor, on the single most important button in the app. New ends measure
     4.72:1 and 6.16:1 against --on-acc. This is the one place the readability
     pass repaints a design-system surface rather than just the ink; the hue
     family is unchanged, the value is deeper. */
  --acc-grad: linear-gradient(145deg,#9E6027,#8A4E1B);
  --err:  #9D4B3C;
  --done: #5F7355;   /* ornament only — see --done-text for the status line */
  --on-acc: #FFF6EA;
  --dis-bg:  #E4DED4;                               /* disabled = FLAT, no shadow */
  /* Disabled text was #B5AA9A = 1.71:1 on --dis-bg — unreadable. WCAG exempts
     disabled controls, but "Export stitch file" greyed out is the label this
     audience most needs to read while they work out why it is off. The design
     system's own rule ("disabled is FLAT, never merely faint") means the
     missing shadow already carries the state, so the ink can be dark. */
  --dis-txt: #655C4E;

  /* depth — MOBILE scale (<768px): offsets shrink so shadows don't eat the gutter */
  --up:  3px 3px 7px rgba(176,162,143,.50), -3px -3px 7px rgba(255,253,249,.95);
  --in2: inset 3px 3px 6px rgba(176,162,143,.60), inset -3px -3px 6px rgba(255,253,249,.90);
  /* README defines only --up/--in2 for mobile; alias the rest rather than invent offsets */
  --up2: var(--up);
  --up3: var(--up);
  --in:  var(--in2);
  /* accent button pressed — inverts INSIDE the gradient, not the surface */
  --acc-press: inset 3px 3px 7px rgba(94,58,20,.5), inset -2px -2px 6px rgba(255,220,180,.35);

  /* textures */
  --weave:
    repeating-linear-gradient(0deg,  rgba(255,255,255,.30) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(90deg, rgba(160,146,127,.10) 0 1px, transparent 1px 3px);
  --satin:
    repeating-linear-gradient(102deg, rgba(255,255,255,.55) 0 1px, rgba(69,60,49,.12) 1px 2px, rgba(255,255,255,.12) 2px 4px),
    linear-gradient(102deg,#D9B083,#C98F4F 45%,#B9793A);
  --stitch-rule: repeating-linear-gradient(90deg, rgba(141,129,114,.5) 0 10px, transparent 10px 18px);
  --knot: radial-gradient(circle at 34% 30%, #FFF2DF, var(--acc) 62%, #7A4B1C);

  /* overlay (help dialog) */
  --scrim: rgba(120,108,92,.32);
  --sheet-shadow: 0 -18px 44px rgba(120,108,92,.38);
  --modal-shadow: 20px 20px 44px rgba(120,108,92,.4), -14px -14px 34px rgba(255,253,249,.7);

  /* type */
  --f-ui:    Outfit, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --f-disp:  "Instrument Serif", Georgia, "Times New Roman", serif;
  --f-mono:  "IBM Plex Mono", ui-monospace, "SFMono-Regular", Consolas, monospace;

  /* space / radius / targets */
  --gutter: 20px;      /* phone */
  --r-ctrl: 16px;      /* control 15–18 */
  --r-card: 26px;      /* card 20–26, by viewport */
  --r-frame: 30px;
  --touch: 48px;       /* floor for anything interactive */
  --touch-lg: 56px;    /* primary buttons, tools */
  --touch-xl: 58px;    /* bottom-pinned primary */
  --t: .16s;           /* press transition, spec .14–.18s */
}

/* Desktop / >=768px depth scale + gutters */
@media (min-width: 768px) {
  :root {
    --up:  8px 8px 17px rgba(176,162,143,.55), -8px -8px 17px rgba(255,253,249,.95);
    --up2: 4px 4px  9px rgba(176,162,143,.50), -4px -4px  9px rgba(255,253,249,.95);
    --up3: 16px 16px 32px rgba(176,162,143,.50), -12px -12px 28px rgba(255,253,249,.92);
    --in:  inset 5px 5px 10px rgba(176,162,143,.60), inset -5px -5px 10px rgba(255,253,249,.95);
    --in2: inset 3px 3px  6px rgba(176,162,143,.60), inset -3px -3px  6px rgba(255,253,249,.90);
    --gutter: 28px;
    --r-card: 22px;
  }
}
@media (min-width: 1024px) {
  :root { --gutter: 56px; --r-card: 18px; }
}

/* ---- BASE ---------------------------------------------------------------- */
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  background-image: var(--weave);
  color: var(--txt);
  font-family: var(--f-ui);
  font-weight: 300;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}

.page {
  max-width: 560px;
  margin: 0 auto;
  padding: 22px var(--gutter) 52px;
  display: flex;
  flex-direction: column;
  gap: 16px;                       /* card vertical gap on phone */
}
/* <main> keeps its own column so it stays a real landmark (no display:contents,
   which drops element semantics in some engines). */
.app-main {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
@media (min-width: 768px) {
  .page { gap: 22px; max-width: 640px; }
  .app-main { gap: 22px; }
}

/* ---- ORNAMENTS ----------------------------------------------------------- */
.rule {                            /* running-stitch rule (section divider) */
  flex: 1;
  height: 3px;
  border-radius: 2px;
  background: var(--stitch-rule);
}
.knot {                            /* French knot marker / bullet */
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--knot);
  box-shadow: 2px 2px 4px rgba(140,124,103,.6);
}

/* ---- TYPE ROLES ---------------------------------------------------------- */
/* Section eyebrow. Spec is 11px / .20em / uppercase. Readability pass:
   13px, tracking cut to .10em (wide tracking measurably hurts low-vision
   reading), and sentence case — these strings run to three or four words and
   all-caps is harder to read at length. The mono face and accent colour keep
   the design signature. */
.eyebrow {
  font: 500 13px/1.3 var(--f-mono);
  letter-spacing: .10em;
  color: var(--acc-text);
  white-space: nowrap;
}
.eyebrow-row {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0 0 12px;
}
.ui-label {                        /* UI label — 15 -> 17px */
  font: 500 17px/1.6 var(--f-ui);
  color: var(--txt);
}
.data {                            /* data / measurement — 12 -> 14px */
  font: 500 14px/1.6 var(--f-mono);
  letter-spacing: .06em;
  color: var(--mut-text);
}

/* ---- HEADER -------------------------------------------------------------- */
/* Three columns: logo tile · titles · help button. The titles block wraps its
   own wordmark/beta/tagline so the help trigger can own a full-height column
   without fighting the old four-area grid. */
/* Two columns: the titles block, and the help button. The placeholder logo tile
   was removed — it was a stand-in "S", and with it in the grid the wordmark was
   forced into a narrow third column and wrapped ABOVE the tile, which read as a
   layout bug. The wordmark is the logo until there is a real mark. */
.app-header {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "titles help"
    "note   note";
  align-items: start;
  column-gap: 14px;
  row-gap: 2px;
  padding: 4px 0 6px;
}
/* Wordmark and Beta stamp share the first line (the stamp wraps under it on very
   narrow screens); the tagline is forced onto its own full row so it never has
   to flow around the stamp — that wrap read as broken. */
.header-titles {
  grid-area: titles;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 10px;
  row-gap: 6px;
  min-width: 0;
}
.header-titles .tagline { flex: 1 0 100%; }
.wordmark {
  margin: 0;
  /* 30px, not the system's 34px, ON PHONES ONLY. Measured at 375px: with the
     60px help button now in the header grid, "Stitch My Signature" wrapped to
     two lines at 34px (78px tall) and drove the header to 322px — 40% of an
     812px screen before you reach the canvas. 30px sets one line (35px tall).
     Full 42px returns at >=768px where there is room. */
  font: 400 30px/1.15 var(--f-disp);
  letter-spacing: -.01em;
  color: var(--txt);
}
.tagline {
  flex: 1 0 100%;
  margin: 0;
  font: 300 17px/1.6 var(--f-ui);  /* body 14 -> 17px */
  color: var(--body);
}

/* ---- HELP TRIGGER -------------------------------------------------------- */
/* 60px target (spec floor 48, primaries 56). A round "i" alone is not a label,
   so the circular glyph sits above the word HELP. COMPROMISE ON RECORD: the
   design language would make this a plain 56px circle; a circle cannot hold a
   readable word, so the target is a rounded square holding a circular glyph. */
.help-btn {
  grid-area: help;
  align-self: center;
  justify-self: end;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-width: 60px;
  min-height: 60px;
  padding: 6px 8px;
  border: 0;
  border-radius: 20px;
  background: var(--srf);
  box-shadow: var(--up);
  cursor: pointer;
  transition: box-shadow var(--t), transform var(--t);
}
.help-btn-glyph {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--srf2);
  box-shadow: var(--in2);
  font: 500 15px/1 var(--f-mono);
  color: var(--acc-text);
}
.help-btn-label {
  font: 500 13px/1 var(--f-mono);
  letter-spacing: .10em;
  color: var(--acc-text);
}
.help-btn:active { box-shadow: var(--in2); }
.help-btn:focus-visible { outline: 3px solid var(--acc-text); outline-offset: 3px; }

/* ---- BETA ---------------------------------------------------------------- */
/* Recessed pill: reads as a stamp pressed into the linen rather than a shouty
   badge. Mono per the data/label rule. Uses the accent, which is the only UI
   colour in the system — no new hue introduced. 10 -> 13px, tracking .20 -> .10em. */
.beta-stamp {
  padding: 5px 11px;
  border-radius: 50px;
  background: var(--srf2);
  box-shadow: var(--in2);
  font: 500 13px/1.3 var(--f-mono);
  letter-spacing: .10em;
  text-transform: uppercase;       /* one word — all-caps is fine at this length */
  color: var(--acc-text);
  white-space: nowrap;
}
.beta-note {
  grid-area: note;
  display: flex;
  align-items: flex-start;
  gap: 9px;
  margin: 12px 0 0;
  padding: 13px 15px;
  border-radius: 15px;
  background: var(--srf2);
  box-shadow: var(--in2);
  font: 300 17px/1.6 var(--f-ui);  /* 13 -> 17px */
  color: var(--body);
}
.beta-note .knot { flex: 0 0 auto; margin-top: 4px; }

@media (min-width: 768px) {
  .wordmark { font-size: 42px; }
  .tagline { font-size: 18px; }
  .beta-stamp { font-size: 13px; padding: 6px 13px; }
  .beta-note { font-size: 18px; }
}

/* ---- CARD ---------------------------------------------------------------- */
.card {
  background: var(--srf);
  border-radius: var(--r-card);
  box-shadow: var(--up2);
  padding: 18px;
}
@media (min-width: 768px) { .card { padding: 22px; } }

/* ---- CANVAS -------------------------------------------------------------- */
/* Inset well, radius 22, weave, dashed inset border at 26px inset.
   NOTE: the weave lives in CSS *behind* the canvas element — never painted into
   the bitmap, because canvasToBool() luma-thresholds those exact pixels. */
.canvas-wrap {
  position: relative;
  border-radius: 22px;
  background: var(--srf2);
  background-image: var(--weave);
  box-shadow: var(--in);
  padding: 10px;
}
/* Dashed inset border, 26px in from the edge of the recessed canvas frame
   (spec measures the inset from the frame, not from the <canvas> box; the
   canvas itself is transparent, so the ring reads through behind the ink). */
.canvas-wrap::after {
  content: "";
  position: absolute;
  inset: 26px;
  border: 2px dashed rgba(141,129,114,.3);
  border-radius: 10px;
  pointer-events: none;
}
#pad {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 2 / 1;             /* matches the 1000x500 raster the tracer reads */
  background: transparent;
  border-radius: 14px;
  touch-action: none;              /* critical: lets pointer-draw work on touch */
  cursor: crosshair;
  position: relative;
  z-index: 1;
}

/* #hint is the ONLY status/error surface — app.js writes long strings here
   (export results, trace failures, resize confirmations). It is READ, not
   glanced at, so it takes the largest lift of any mono role: 12 -> 15px.
   Must wrap freely: no nowrap, no clipping, no fixed height. */
/* The primary instruction on the page — it tells a first-time user what to do
   at all. Promoted from 15px mono/--mut-text to 18px UI/--txt: mono at a small
   size with tracking is the least legible combination in the system, and this
   audience is often low-vision. Ranked above the buttons it introduces. */
.hint {
  margin: 14px 2px 0;
  font: 400 18px/1.6 var(--f-ui);   /* 1.6 is the enforced prose floor */
  color: var(--txt);
  text-align: center;
  overflow-wrap: break-word;
}
@media (min-width: 768px) { .hint { font-size: 19px; } }

/* ---- BUTTONS ------------------------------------------------------------- */
.row { display: flex; gap: 12px; }
.actions-row { margin-top: 14px; }

.btn {
  flex: 1;
  min-height: var(--touch-lg);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font: 500 17px/1.3 var(--f-ui);  /* 15 -> 17px */
  color: var(--txt);
  background: var(--srf);
  border: 0;
  border-radius: var(--r-ctrl);
  box-shadow: var(--up);
  cursor: pointer;
  padding: 0 14px;
  transition: box-shadow var(--t), transform var(--t), color var(--t);
}
.btn-glyph {
  font: 400 18px/1 var(--f-mono);
  color: var(--acc-text);
}
/* Press = raised -> inset. The primary affordance on touch. */
.btn:active { box-shadow: var(--in2); }
.btn:focus-visible { outline: 3px solid var(--acc-text); outline-offset: 3px; }

/* Primary (accent) */
.btn-primary {
  background: var(--acc-grad);
  color: var(--on-acc);
  box-shadow: var(--up2);
  min-height: var(--touch-xl);
  border-radius: 19px;
  font-size: 18px;
  width: 100%;
}
.btn-primary .btn-glyph { color: var(--on-acc); }
.btn-primary:active {
  box-shadow: var(--acc-press);
  transform: translateY(1px);
}

/* Disabled = FLAT (no shadow), never merely faint. */
.btn:disabled,
.btn[disabled] {
  background: var(--dis-bg);
  color: var(--dis-txt);
  box-shadow: none;
  cursor: not-allowed;
  transform: none;
}
.btn:disabled .btn-glyph,
.btn[disabled] .btn-glyph { color: var(--dis-txt); }

/* The two ways IN — "Sign big" and "Upload image" — stacked and IDENTICALLY
   sized. Equal weight on purpose: drawing and uploading are equally valid
   starts, and a wider or bolder one would read as the "right" choice.
   LAYOUT ONLY — both inherit .btn's colour, so no new text pairing enters the
   contrast table. */
.entry-actions {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 14px;
}
.btn-entry {
  width: 100%;
  flex: 0 0 auto;          /* cancel .btn's flex:1 — these stack, not share a row */
  min-height: var(--touch-lg);
}
.btn-signbig { width: 100%; }
.signbig-cta {
  margin: 8px 2px 0;
  font: 300 15px/1.6 var(--f-ui);
  color: var(--body);
  text-align: center;
}

/* ---- PRESETS (segmented control: inset container, raised selected) -------- */
.preset-row {
  padding: 8px;
  gap: 8px;
  border-radius: 20px;
  background: var(--srf2);
  box-shadow: var(--in2);
}
.preset {
  flex: 1;
  min-height: 96px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 7px;
  background: transparent;
  border: 0;
  border-radius: var(--r-ctrl);
  box-shadow: none;
  color: var(--mut-text);
  cursor: pointer;
  padding: 10px 4px;
  transition: box-shadow var(--t), color var(--t), background var(--t);
}
.preset-name { font: 500 17px/1.3 var(--f-ui); }      /* 14 -> 17px */
.preset-sub {
  font: 400 13px/1.3 var(--f-mono);                   /* 10 -> 13px */
  letter-spacing: .10em;
  text-transform: uppercase;       /* one word — all-caps is fine at this length */
  color: var(--mut-text);
}
.preset:focus-visible { outline: 3px solid var(--acc-text); outline-offset: 2px; }
/* Selected segment: raised surface + accent text. Colour + weight carry the
   state alongside the shadow — the shadow is never the only signal. */
.preset.is-active {
  background: var(--srf);
  box-shadow: var(--up2);
  color: var(--acc-text);
}
.preset.is-active .preset-name { color: var(--acc-text); }
.preset.is-active .preset-sub { color: var(--acc-text); }
.preset:active { box-shadow: var(--in2); }

/* Pen-width demo swatches, rendered in satin thread texture. */
.preset-glyph {
  display: block;
  width: 44px;
  background: var(--satin);
  box-shadow:
    1px 1px 3px rgba(140,124,103,.55),
    inset 0 1px 1px rgba(255,255,255,.45),
    inset 0 -2px 3px rgba(69,60,49,.18);
}
.glyph-pencil { height: 4px;  border-radius: 2px; margin: 9px 0; }
.glyph-marker { height: 13px; border-radius: 7px; margin: 4px 0; }
.glyph-callig {
  height: 13px; border-radius: 7px; margin: 4px 0;
  clip-path: polygon(0 40%, 30% 0, 65% 100%, 100% 30%, 100% 70%, 65% 100%, 30% 100%, 0 60%);
  transform: skewX(-12deg);
}

/* ---- SIZE CONTROL -------------------------------------------------------- */
.hoop-picker { margin: 0 0 14px; }
.hoop-label {
  display: block;
  margin: 0 0 10px;
  font: 500 17px/1.6 var(--f-ui);  /* 15 -> 17px */
  color: var(--txt);
}
.hoop-select {
  width: 100%;
  min-height: var(--touch-lg);
  font: 500 17px/1.3 var(--f-ui);  /* >=16px or iOS zooms the viewport on focus */
  color: var(--txt);
  background: var(--srf);
  border: 0;
  border-radius: var(--r-ctrl);
  box-shadow: var(--up);
  padding: 0 46px 0 16px;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  /* chevron drawn as an inline SVG so no image asset is needed (%237F4F1E = --acc-text) */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 16 16'%3E%3Cpath fill='%237F4F1E' d='M4 6l4 4 4-4z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 18px center;
  transition: box-shadow var(--t);
}
.hoop-select:focus-visible { outline: 3px solid var(--acc-text); outline-offset: 2px; }
.hoop-select:active { box-shadow: var(--in2); }

/* Big live readout: inches large (mono numerals), mm secondary. Inset = it
   holds state rather than inviting a press. */
.size-readout {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 18px;
  padding: 14px 18px;
  border-radius: 18px;
  background: var(--srf2);
  box-shadow: var(--in2);
}
.size-line { display: inline-flex; align-items: baseline; gap: 8px; }
.size-key {
  font: 500 13px/1.3 var(--f-mono);                   /* 11 -> 13px */
  letter-spacing: .10em;                              /* .16 -> .10em */
  text-transform: uppercase;       /* a single letter (W / H) */
  color: var(--mut-text);
}
.size-val {
  font: 500 26px/1.2 var(--f-mono);
  letter-spacing: -.02em;
  color: var(--acc-text);
}
.size-mm {
  font: 400 14px/1.6 var(--f-mono);                   /* 12 -> 14px */
  letter-spacing: .06em;
  color: var(--mut-text);
}
.size-line-h .size-val { color: var(--txt); }

/* Live fit indicator. There is no "warning" tier in the palette (one accent,
   error red, done green), and Export is NEVER blocked — so "too big" reads in
   the accent's darkened text tone on a recessed surface: clear, not alarming.
   Deliberate mapping: fits -> --done-text, warn -> --acc-text, off -> --mut-text.
   This is the most-read status line in the app, so it takes body size (17px),
   not the 13px it used to run at. */
.hoop-fit {
  margin: 12px 0 0;
  padding: 13px 16px;
  border-radius: 16px;
  background: var(--srf2);
  box-shadow: var(--in2);
  font: 500 17px/1.6 var(--f-ui);  /* 13 -> 17px */
  overflow-wrap: break-word;
}
.hoop-fit[data-state="fits"] { color: var(--done-text); }
.hoop-fit[data-state="warn"] { color: var(--acc-text); }
.hoop-fit[data-state="off"]  { color: var(--mut-text); }
.hoop-fit:empty { display: none; }
.hoop-fit-sub {
  display: block;
  margin-top: 6px;
  font: 400 14px/1.6 var(--f-mono);                   /* 11 -> 14px */
  letter-spacing: .06em;
  color: var(--mut-text);
}

/* "Resize to fit" — deliberately NOT .chip (that selector owns the
   data-inches quick presets and gets click handlers in app.js). */
.btn-fit-resize {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  min-height: var(--touch-lg);
  margin-top: 10px;
  font: 500 17px/1.3 var(--f-ui);  /* 14 -> 17px */
  color: var(--acc-text);
  background: var(--srf);
  border: 0;
  border-radius: var(--r-ctrl);
  box-shadow: var(--up);
  cursor: pointer;
  transition: box-shadow var(--t);
}
.btn-fit-resize:active { box-shadow: var(--in2); }
.btn-fit-resize:focus-visible { outline: 3px solid var(--acc-text); outline-offset: 3px; }
.btn-fit-resize[hidden] { display: none; }

/* Quick size presets — segmented control treatment. */
.size-chips {
  margin-top: 14px;
  gap: 8px;
  padding: 8px;
  border-radius: 20px;
  background: var(--srf2);
  box-shadow: var(--in2);
}
.chip {
  flex: 1;
  min-height: var(--touch);
  border: 0;
  border-radius: 15px;
  background: transparent;
  box-shadow: none;
  color: var(--mut-text);
  font: 500 15px/1.3 var(--f-mono);                   /* 13 -> 15px */
  letter-spacing: .06em;
  cursor: pointer;
  transition: box-shadow var(--t), color var(--t), background var(--t);
}
.chip.is-active {
  background: var(--srf);
  box-shadow: var(--up2);
  color: var(--acc-text);
}
.chip:active { box-shadow: var(--in2); }
.chip:focus-visible { outline: 3px solid var(--acc-text); outline-offset: 2px; }

/* Width slider: inset track 28px (mobile), 34px raised knob, accent gradient
   fill driven by the --fill percentage app.js sets on every update. */
.size-slider {
  width: 100%;
  height: 34px;
  margin: 18px 0 8px;
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  cursor: pointer;
  touch-action: none;
  --fill: 30%;
}
.size-slider:focus { outline: none; }
.size-slider:focus-visible { outline: 3px solid var(--acc-text); outline-offset: 4px; border-radius: 14px; }

.size-slider::-webkit-slider-runnable-track {
  height: 28px;
  border-radius: 14px;
  background-color: var(--srf);
  background-image: var(--acc-grad);
  background-repeat: no-repeat;
  background-size: var(--fill) 100%;
  box-shadow: var(--in2);
}
.size-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 34px;
  height: 34px;
  margin-top: -3px;
  border: 0;
  border-radius: 50%;
  background: linear-gradient(145deg,#F4EEE5,#DDD4C7);
  box-shadow: 3px 3px 7px rgba(163,148,130,.65), -3px -3px 7px rgba(255,253,249,.95);
}
.size-slider::-moz-range-track {
  height: 28px;
  border-radius: 14px;
  background: var(--srf);
  box-shadow: var(--in2);
}
.size-slider::-moz-range-progress {
  height: 28px;
  border-radius: 14px 0 0 14px;
  background: var(--acc-grad);
}
.size-slider::-moz-range-thumb {
  width: 34px;
  height: 34px;
  border: 0;
  border-radius: 50%;
  background: linear-gradient(145deg,#F4EEE5,#DDD4C7);
  box-shadow: 3px 3px 7px rgba(163,148,130,.65), -3px -3px 7px rgba(255,253,249,.95);
}

.size-hoop-hint {
  margin: 6px 2px 0;
  font: 400 14px/1.6 var(--f-mono);                   /* 11 -> 14px, was --fnt */
  letter-spacing: .06em;
  color: var(--mut-text);
}

/* ---- SINGLE-COLOUR NOTE -------------------------------------------------- */
/* The swatch is real thread data (it IS the stroke colour), so it carries the
   satin sheen overlay the spec reserves for floss. */
.color-note {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
}
.swatch {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background:
    repeating-linear-gradient(96deg,
      rgba(255,255,255,.42) 0 1px,
      rgba(0,0,0,.12) 1px 2px,
      transparent 2px 4px),
    #2A2438;
  box-shadow: 2px 2px 5px rgba(140,124,103,.55), inset 0 1px 1px rgba(255,255,255,.25);
}
.color-text { font: 300 17px/1.6 var(--f-ui); color: var(--body); }  /* 14 -> 17px */
.color-text strong { font-weight: 500; color: var(--txt); }

/* ---- EXPORT BLOCK -------------------------------------------------------- */
.export-block { display: flex; flex-direction: column; gap: 10px; margin-top: 2px; }
/* DST-only MVP: the format picker is gone from the UI; this mono line tells the
   user exactly what they get. NOT .chip — no click handler must ever bind here.
   11 -> 14px, tracking .14 -> .10em, and the ALL-CAPS dropped: "works on most
   machines" is a sentence, and sentences are harder to read in caps. */
.fmt-note {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 0;
  font: 500 14px/1.6 var(--f-mono);
  letter-spacing: .10em;
  color: var(--mut-text);
}
.fmt-note .knot { width: 12px; height: 12px; }
.btn-preview { width: 100%; }

/* "Send file" — the second half of the journey (get the .dst off the iPad).
   LAYOUT ONLY, on top of .btn-primary, so it adds no new text pairing. Both it
   and its note ship hidden and are revealed by app.js after a real export;
   [hidden] needs an explicit rule because .btn is display:flex. */
.btn-share { width: 100%; }
.btn-share[hidden] { display: none; }
.share-note {
  margin: 0 2px;
  font: 300 17px/1.6 var(--f-ui);
  color: var(--body);
  text-align: center;
  overflow-wrap: break-word;
}
.share-note[hidden] { display: none; }
.export-note {
  margin: 2px 2px 0;
  font: 300 17px/1.6 var(--f-ui);  /* 13 -> 17px */
  color: var(--body);
  text-align: center;
}

/* ==========================================================================
   HELP DIALOG — bottom sheet on phones, centred modal >=768px.
   The overlay IS the scrim. [hidden] needs an explicit rule because the
   overlay is display:flex, which would otherwise beat the hidden attribute.
   ======================================================================== */
.help-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: flex-end;           /* bottom sheet on phones */
  justify-content: center;
  background: var(--scrim);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  animation: scrimin .2s ease-out;
}
.help-overlay[hidden] { display: none; }

.help-sheet {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 520px;
  max-height: 92vh;
  max-height: 92dvh;               /* dvh keeps the Close button off the iOS toolbar */
  background: var(--srf);
  border-radius: 32px 32px 0 0;    /* sheet radius, per the mobile spec */
  box-shadow: var(--up3), var(--sheet-shadow);
  animation: sheetup .24s cubic-bezier(.2,.9,.3,1);
  outline: none;
}
.help-sheet:focus-visible { outline: 3px solid var(--acc-text); outline-offset: -6px; }

.help-head {
  flex: 0 0 auto;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 18px 18px 14px;
  border-bottom: 3px solid transparent;
  background-image: var(--stitch-rule);
  background-repeat: no-repeat;
  background-position: 18px 100%;
  background-size: calc(100% - 36px) 3px;
}
.help-title {
  flex: 1;
  margin: 0;
  align-self: center;
  font: 400 28px/1.2 var(--f-disp);
  color: var(--txt);
}
/* 56px close control, top-right — spec floor for a primary touch target. */
.help-x {
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: var(--srf);
  box-shadow: var(--up);
  font: 500 22px/1 var(--f-mono);
  color: var(--txt);
  cursor: pointer;
  transition: box-shadow var(--t);
}
.help-x:active { box-shadow: var(--in2); }
.help-x:focus-visible { outline: 3px solid var(--acc-text); outline-offset: 3px; }

/* The scrollable region — the content is long and must work on a small screen. */
.help-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding: 4px 18px 8px;
}
.help-body:focus-visible { outline: 3px solid var(--acc-text); outline-offset: -3px; }

.help-lead {
  margin: 16px 0 4px;
  font: 300 17px/1.6 var(--f-ui);
  color: var(--body);
}
.help-sec { margin: 0 0 6px; }
.help-h {
  margin: 22px 0 8px;
  font: 500 19px/1.4 var(--f-ui);
  color: var(--txt);
}
.help-sub {
  margin: 18px 0 6px;
  font: 500 17px/1.6 var(--f-ui);
  color: var(--acc-text);
}
.help-list {
  margin: 0;
  padding-left: 26px;
  font: 300 17px/1.65 var(--f-ui);
  color: var(--body);
}
.help-list li { margin: 0 0 10px; }
.help-list strong { font-weight: 600; color: var(--txt); }
.help-aside {
  margin: 10px 0 0;
  padding: 12px 14px;
  border-radius: 15px;
  background: var(--srf2);
  box-shadow: var(--in2);
  font: 300 17px/1.6 var(--f-ui);
  color: var(--body);
}
.help-aside strong { font-weight: 600; color: var(--txt); }
.help-reassure {
  margin-top: 24px;
  padding: 14px 16px;
  border-radius: 18px;
  background: var(--srf2);
  box-shadow: var(--in2);
}
.help-reassure p {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 0 0 10px;
  font: 300 17px/1.6 var(--f-ui);
  color: var(--body);
}
.help-reassure p:last-child { margin-bottom: 0; }
.help-reassure .knot { margin-top: 4px; }

/* Full-width Close at the bottom: older users routinely miss a corner ✕. */
.help-foot {
  flex: 0 0 auto;
  padding: 14px 18px calc(18px + env(safe-area-inset-bottom, 0px));
}
.help-close-btn { min-height: var(--touch-xl); }

@keyframes sheetup { from { transform: translateY(28px); opacity: .6; } }
@keyframes floatin { from { transform: translateY(6px); opacity: .6; } }
@keyframes scrimin { from { opacity: 0; } }

/* >=768px: centred modal, card radius, deeper modal shadow. */
@media (min-width: 768px) {
  .help-overlay { align-items: center; padding: 24px; }
  .help-sheet {
    max-width: 560px;
    max-height: 86vh;
    max-height: 86dvh;
    border-radius: var(--r-card);
    box-shadow: var(--modal-shadow);
    animation: floatin .22s cubic-bezier(.2,.9,.3,1);
  }
  .help-head, .help-body { padding-left: 24px; padding-right: 24px; }
  .help-head { background-position: 24px 100%; background-size: calc(100% - 48px) 3px; }
  .help-foot { padding: 16px 24px 22px; }
  .help-title { font-size: 32px; }
  .help-list, .help-lead { font-size: 18px; }
}

/* ==========================================================================
   BIG SIGNING SURFACE — full-screen mode for #pad.

   THE ONE RULE HERE: this is presentation only. app.js MOVES the same
   <canvas id="pad"> into #signbig-stage; its 1000x500 backing store is never
   touched, so there is exactly one raster and one draw path. Everything below
   only changes how large that one canvas is DISPLAYED.
   ======================================================================== */
.signbig-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;                    /* above the help dialog (100) */
  height: 100vh;
  height: 100dvh;                  /* dvh keeps the buttons off the iOS toolbar */
  background: var(--bg);
  background-image: var(--weave);
  overflow: hidden;                /* nothing scrolls behind the signing area */
}
.signbig-overlay[hidden] { display: none; }

.signbig-panel {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  outline: none;
  animation: floatin .2s cubic-bezier(.2,.9,.3,1);
}
.signbig-panel:focus-visible { outline: 3px solid var(--acc-text); outline-offset: -6px; }

/* Slim top bar. In landscape it is title-only; in portrait the rotate hint
   sits beneath it IN NORMAL FLOW, so it can never cover the signing area. */
.signbig-bar {
  flex: 0 0 auto;
  position: relative;
  z-index: 1;
  padding: 10px var(--gutter);
  background: var(--srf);
  box-shadow: var(--up2);
}
.signbig-title {
  margin: 0;
  font: 400 24px/1.25 var(--f-disp);
  color: var(--txt);
}
/* Friendly, never blocking: rotation lock is common, so this is a suggestion.
   app.js hides it the moment matchMedia reports landscape. display:flex would
   beat the hidden attribute, hence the explicit [hidden] rule. */
.signbig-hint {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  margin: 9px 0 2px;
  padding: 11px 14px;
  border-radius: 15px;
  background: var(--srf2);
  box-shadow: var(--in2);
  font: 300 17px/1.6 var(--f-ui);
  color: var(--body);
}
.signbig-hint[hidden] { display: none; }
.signbig-hint .knot { flex: 0 0 auto; margin-top: 4px; }

/* The signing area: every pixel left over after the bar and the buttons. */
.signbig-stage {
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  display: grid;
  place-items: center;
  padding: 10px var(--gutter);
  background: var(--srf2);
  background-image: var(--weave);
  box-shadow: var(--in);
}

/* CONTAIN, not stretch. The canvas keeps its 2/1 aspect (from the base #pad
   rule) so what you draw is what gets traced; letterboxing is fine.
   Two layers on purpose:
     1. width:100% + max-height:100% + aspect-ratio — the plain path.
     2. width:min(100cqw,200cqh) — plain arithmetic against the stage's own box,
        which also lets the canvas scale UP past its 1000x500 intrinsic size on
        a large iPad. No reliance on subtle max-height/aspect-ratio transfer.
   Even in the worst case this is a fidelity question, never a correctness one:
   pos() in app.js divides by getBoundingClientRect().width/.height separately,
   so pointer -> raster mapping stays exact whatever size the box ends up. */
body.signbig-open #pad {
  width: 100%;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  border-radius: 18px;
  background: var(--srf);          /* CSS only — never enters the bitmap */
  box-shadow: var(--in2);
}
@supports (container-type: size) {
  .signbig-stage { container-type: size; }
  body.signbig-open #pad { width: min(100cqw, 200cqh); }
}

/* #pad is .canvas-wrap's only child, so moving it out would collapse the card
   to a 20px sliver and shove everything below it up the page — and #page is
   still laid out behind the overlay. On close the user would come back to a
   different scroll position, which for this audience reads as "it lost my
   signature". The wrap therefore holds exactly the space the canvas occupied
   (same 2/1 ratio) while the canvas is away, so nothing reflows at all.
   The dashed guide ring is inset 26px and would invert on a padding-only box,
   so it stands down too. */
body.signbig-open .canvas-wrap { aspect-ratio: 2 / 1; padding: 0; }
body.signbig-open .canvas-wrap::after { display: none; }

/* Controls at the BOTTOM so they are thumb-reachable on a phone. */
.signbig-foot {
  flex: 0 0 auto;
  position: relative;
  z-index: 1;
  display: flex;
  gap: 12px;
  padding: 12px var(--gutter) calc(12px + env(safe-area-inset-bottom, 0px));
  background: var(--srf);
  box-shadow: var(--up2);
}
.btn-signbig-clear { flex: 1 1 38%; }
/* .btn-primary sets width:100%; inside this flex row it must share. */
.btn-signbig-done { flex: 2 1 62%; width: auto; }

/* Landscape: shrink the chrome so the signing area gets the room. The buttons
   are NOT shrunk — they stay above the 56px floor. */
@media (orientation: landscape) {
  .signbig-bar { padding-top: 7px; padding-bottom: 7px; }
  .signbig-title { font-size: 20px; }
  .signbig-foot { padding-top: 9px; padding-bottom: calc(9px + env(safe-area-inset-bottom, 0px)); }
}

/* ---- HOVER: >=1024px only, and never the sole signal ---------------------- */
@media (min-width: 1024px) and (hover: hover) {
  .btn:hover:not(:disabled),
  .btn-fit-resize:hover,
  .help-btn:hover { transform: translateY(-1px); }
  .preset:hover:not(.is-active) .preset-name,
  .chip:hover:not(.is-active) { color: var(--acc-text); }
  .btn:hover:not(:disabled):not(.btn-primary) { color: var(--acc-text); }
}

/* ---- REDUCED MOTION ------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .btn:hover, .btn-primary:active, .btn-fit-resize:hover, .help-btn:hover { transform: none !important; }
  .help-sheet, .help-overlay { animation: none !important; }
  .signbig-overlay, .signbig-panel { animation: none !important; }
}
