/*
  Visual direction: the packing table.
  Type is monospace wherever the page shows data a seller has to scan - counts,
  order IDs, page numbers, sizes - because that is the vernacular of manifests,
  thermal receipts and barcodes. Prose is set in the system sans.
  The one bold element is the crop-mark frame: four registration ticks that
  bracket the drop zone and every detected label. It is the thing the tool does.
*/

/*
 * The hidden attribute must always win.
 *
 * The browser's own rule is `[hidden] { display: none }`, which any class
 * setting `display` outranks - so an element marked hidden in the markup stays
 * on screen the moment it also carries a layout class. That put a receipt with
 * em-dashes where the plan name should be in front of a seller who had just
 * paid, and it is the same trap as an invisible overlay that still swallows
 * taps. There are 166 display rules in this stylesheet; auditing them one by
 * one would fix today's instance and none of the next ones.
 *
 * !important is right here. This is not styling, it is the meaning of the
 * attribute, and there is no case where markup says hidden and we want it
 * seen. Anything that needs to animate out uses a class instead.
 */
[hidden] { display: none !important; }


:root {
  --paper:   #e9edf0;
  --card:    #ffffff;
  --ink:     #0d1b2a;
  --ink-2:   #5a6b7b;
  --ink-3:   #8c9ba8;
  --rule:    #c9d3db;
  --rule-2:  #dde4e9;
  --shimmer: rgba(255, 255, 255, 0.7);
  --signal:  #d62246;
  --ok:      #1e7a5a;
  --warn:    #9a6700;

  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;

  --step: 4px;
  --radius: 3px;
}

/*
  The theme does not follow the operating system. Sellers print from this in
  daylight and the tool pages are documents, so light is the default and dark is
  an explicit choice, remembered per browser. All dark values live under
  [data-theme="dark"] below.
*/

/* Explicit theme choice beats the OS preference, in both directions. The same
   tokens as above, so components never need to know which theme is active. */
:root[data-theme="dark"] {
    --paper:  #0b1218;
    --card:   #131c24;
    --ink:    #e6edf3;
    --ink-2:  #9fb0bd;
    --ink-3:  #6b7c8a;
    --rule:   #2a3742;
    --rule-2: #1e2933;
    --shimmer: rgba(255, 255, 255, 0.07);
    --signal: #ff5a72;
    --ok:     #4bbf8f;
    --warn:   #d4a017;
}
:root[data-theme="light"] {
  --paper:   #e9edf0;
  --card:    #ffffff;
  --ink:     #0d1b2a;
  --ink-2:   #5a6b7b;
  --ink-3:   #8c9ba8;
  --rule:    #c9d3db;
  --rule-2:  #dde4e9;
  --shimmer: rgba(255, 255, 255, 0.7);
  --signal:  #d62246;
  --ok:      #1e7a5a;
  --warn:    #9a6700;

  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;

  --step: 4px;
  --radius: 3px;
}

* { box-sizing: border-box; }

/* A panel wider than the screen must not make the whole page scroll
   sideways - on a phone that reads as the layout being broken. */
body { overflow-x: clip; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* ---------- structural type ---------- */

.eyebrow {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
  display: flex;
  align-items: center;
  gap: 8px;
}
.eyebrow::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--rule);
}
.eyebrow .num { color: var(--signal); font-weight: 700; }

.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* ---------- shell ---------- */

.masthead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 18px 24px;
  border-bottom: 1px solid var(--rule);
  background: var(--card);
}

.wordmark {
  font-family: var(--mono);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  margin: 0;
}
.wordmark .bars {
  color: var(--signal);
  letter-spacing: -1px;
  margin-left: 6px;
}

.masthead p { margin: 0; font-size: 13px; color: var(--ink-2); }

.layout {
  display: grid;
  grid-template-columns: 360px minmax(0, 1fr);
  gap: 0;
  align-items: stretch;
  min-height: calc(100vh - 140px);
}

.controls {
  border-right: 1px solid var(--rule);
  background: var(--card);
  padding: 20px 22px 40px;
  /* Full height and pinned: the rail should read as a side of the app, not as
     a card that runs out partway down the page. */
  align-self: start;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}

.stage { padding: 22px 26px 40px; min-width: 0; }

.block + .block { margin-top: 26px; }
.block > .eyebrow { margin-bottom: 12px; }

/* ---------- crop-mark frame: the signature ---------- */

.cropframe { position: relative; }
.cropframe::before,
.cropframe::after,
.cropframe > .tick-b::before,
.cropframe > .tick-b::after {
  content: "";
  position: absolute;
  width: 14px;
  height: 14px;
  border: 2px solid var(--signal);
  transition: inset 160ms ease, opacity 160ms ease;
}
.cropframe::before      { top: -1px; left: -1px;  border-right: 0; border-bottom: 0; }
.cropframe::after       { top: -1px; right: -1px; border-left: 0;  border-bottom: 0; }
.cropframe > .tick-b::before { bottom: -1px; left: -1px;  border-right: 0; border-top: 0; }
.cropframe > .tick-b::after  { bottom: -1px; right: -1px; border-left: 0;  border-top: 0; }

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

.dropzone {
  aspect-ratio: 4 / 6;
  width: min(340px, 100%);
  margin: 4vh auto 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  text-align: center;
  padding: 24px;
  background: var(--card);
  border: 1px dashed var(--rule);
  cursor: pointer;
}
.dropzone:hover, .dropzone.is-over { border-color: var(--signal); }
.dropzone.is-over::before, .dropzone.is-over::after,
.dropzone.is-over > .tick-b::before, .dropzone.is-over > .tick-b::after { inset: auto; }
.dropzone.is-over::before { top: 7px; left: 7px; }
.dropzone.is-over::after  { top: 7px; right: 7px; }
.dropzone.is-over > .tick-b::before { bottom: 7px; left: 7px; }
.dropzone.is-over > .tick-b::after  { bottom: 7px; right: 7px; }

.dropzone h2 {
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin: 0;
}
.dropzone p { margin: 0; font-size: 13px; color: var(--ink-2); max-width: 24ch; }
.dropzone .ratio {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-3);
  letter-spacing: 0.1em;
}

.stage-intro { max-width: 46ch; margin: 0 auto; text-align: center; }
.stage-intro h1 {
  font-family: var(--mono);
  font-size: clamp(20px, 3vw, 28px);
  line-height: 1.25;
  letter-spacing: -0.01em;
  margin: 0 0 10px;
}
.stage-intro p { color: var(--ink-2); margin: 0; }

/* ---------- file list ---------- */

.filelist { list-style: none; margin: 0; padding: 0; }
.filelist li {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--rule-2);
  font-size: 13px;
}
.filelist .fname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.filelist .fpages { font-family: var(--mono); font-size: 11px; color: var(--ink-3); }

/* ---------- settings ---------- */

.setting {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 7px 0;
}
.setting label { font-size: 14px; cursor: pointer; }
.setting .hint { display: block; font-size: 12px; color: var(--ink-3); margin-top: 1px; }
.setting input[type="checkbox"] { margin-top: 3px; accent-color: var(--signal); width: 15px; height: 15px; }

.setting-row { padding: 7px 0; }
.setting-row > span { display: block; font-size: 14px; margin-bottom: 5px; }

select, input[type="text"], input[type="number"] {
  width: 100%;
  font: inherit;
  font-size: 14px;
  padding: 7px 9px;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

/* ---------- buttons ---------- */

.btn {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 11px 16px;
  border: 1px solid var(--ink);
  border-radius: var(--radius);
  background: var(--ink);
  color: var(--card);
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.btn:hover:not(:disabled) { background: var(--signal); border-color: var(--signal); color: #fff; }
.btn:disabled { opacity: 0.35; cursor: not-allowed; }
.btn--full { width: 100%; }
.btn--quiet {
  background: transparent;
  color: var(--ink-2);
  border-color: var(--rule);
}
.btn--quiet:hover:not(:disabled) { color: var(--signal); border-color: var(--signal); background: transparent; }
.btn--tiny { padding: 4px 8px; font-size: 10px; }

:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }

/* ---------- stats strip ---------- */

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
  margin-bottom: 22px;
}
.stat { background: var(--card); padding: 11px 13px; }
.stat b {
  display: block;
  font-family: var(--mono);
  font-size: 21px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.stat span {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* ---------- proof sheet ---------- */

.proof { display: block; }
.proofgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 18px;
}
.proofmore { display: block; margin: 20px auto 0; }
.proofmore.hidden { display: none; }

.thumb { background: var(--card); border: 1px solid var(--rule); padding: 9px; }
/* overflow:hidden is load-bearing: .cropmark dims the page with a 9999px
   box-shadow, which without clipping would darken the whole viewport once per
   thumbnail. */
.thumb .shot { position: relative; background: #fff; line-height: 0; overflow: hidden; }
.thumb canvas { width: 100%; height: auto; display: block; }
.thumb .meta {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 6px;
  margin-top: 7px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.badge { padding: 1px 5px; border: 1px solid currentColor; border-radius: 2px; }
.badge--label    { color: var(--ok); }
.badge--invoice  { color: var(--ink-3); }
.badge--combined { color: var(--warn); }
.badge--blank    { color: var(--signal); }

/* crimson rectangle showing exactly what will be kept */
.cropmark {
  position: absolute;
  border: 1.5px solid var(--signal);
  pointer-events: none;
  box-shadow: 0 0 0 9999px rgba(13, 27, 42, 0.42);
}

/* ---------- manual crop editor ---------- */

.cropedit { background: var(--card); border: 1px solid var(--rule); padding: 16px; margin-bottom: 22px; }
.cropedit .surface {
  position: relative; display: inline-block; line-height: 0;
  cursor: crosshair; user-select: none; overflow: hidden;
}
.cropedit canvas { display: block; max-width: 100%; height: auto; }
.cropedit .hintbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-top: 12px;
  font-size: 13px; color: var(--ink-2);
}

/* ---------- notices ---------- */

.notice {
  border-left: 3px solid var(--signal);
  background: var(--card);
  padding: 10px 13px;
  font-size: 13px;
  margin-bottom: 16px;
}
.notice--ok { border-left-color: var(--ok); }

.progress { height: 3px; background: var(--rule); overflow: hidden; margin-top: 14px; }
.progress i { display: block; height: 100%; background: var(--signal); width: 0; transition: width 140ms linear; }
.progress-label { font-family: var(--mono); font-size: 11px; color: var(--ink-3); margin-top: 6px; letter-spacing: 0.06em; }

.privacy {
  font-size: 12px;
  color: var(--ink-3);
  border-top: 1px solid var(--rule-2);
  margin-top: 22px;
  padding-top: 12px;
}
.privacy b { color: var(--ink-2); font-weight: 600; }

.hidden { display: none !important; }

@media (max-width: 860px) {
  .layout { grid-template-columns: 1fr; }
  .controls { position: static; max-height: none; border-right: 0; border-bottom: 1px solid var(--rule); }
}

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

/* ============================================================
   Site shell: masthead, menu, home, tools directory
   ============================================================ */

.masthead {
  position: relative;
  z-index: 20;
  gap: 8px 24px;
  align-items: center;
}

.brand { text-decoration: none; color: inherit; flex: none; }
.brand .wordmark { display: inline-flex; align-items: baseline; }

/* The old masthead menu (.menu / .menutoggle / .menupanel) was replaced by the
   l-* header built in ui/nav.js. Its rules are gone; the mobile drawer now
   lives with the rest of that header in landing.css. */
/* ---------- home ---------- */

.home { padding: 0 24px 72px; }

.hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 48px;
  align-items: center;
  padding: 56px 0 44px;
  border-bottom: 1px solid var(--rule);
}
@media (max-width: 860px) { .hero { grid-template-columns: 1fr; gap: 32px; } }

.hero h1 {
  font-family: var(--mono);
  font-size: clamp(26px, 4.2vw, 44px);
  line-height: 1.15;
  letter-spacing: -0.02em;
  margin: 0 0 14px;
  max-width: 18ch;
}
.hero h1 em { font-style: normal; color: var(--signal); }
.hero p { margin: 0 0 22px; font-size: 16px; color: var(--ink-2); max-width: 52ch; }
.hero .cta { display: flex; gap: 10px; flex-wrap: wrap; }

/* A 4:6 label standing in for the thing the whole site is about. */
.herolabel {
  width: 208px;
  aspect-ratio: 4 / 6;
  background: var(--card);
  border: 1px solid var(--rule);
  padding: 18px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.herolabel .rule { height: 1px; background: var(--rule-2); }
.herolabel .line { height: 6px; background: var(--rule-2); border-radius: 1px; }
.herolabel .line.short { width: 55%; }
.herolabel .line.mid { width: 78%; }
.herolabel .barcode { display: flex; align-items: flex-end; gap: 2px; height: 52px; margin-top: auto; }
.herolabel .barcode i { display: block; flex: 1; background: var(--ink); height: 100%; }
.herolabel .barcode i:nth-child(3n) { opacity: 0.35; }
.herolabel .barcode i:nth-child(4n) { opacity: 0.7; }
.herolabel .cap {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.catgrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
  margin-top: 40px;
}
.cat { background: var(--card); padding: 20px 20px 22px; }
.cat h2 {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin: 0 0 4px;
}
.cat > p { margin: 0 0 14px; font-size: 13px; color: var(--ink-3); }
.cat ul { list-style: none; margin: 0; padding: 0; }
.cat li { padding: 4px 0; font-size: 13.5px; border-bottom: 1px solid var(--rule-2); }
.cat li:last-child { border-bottom: 0; }
.cat a { color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--signal); }
.cat a:hover { color: var(--signal); }
.cat .muted { color: var(--ink-3); }
.cat .more { font-family: var(--mono); font-size: 11px; color: var(--ink-3); padding-top: 8px; display: block; }

/* ---------- tools directory ---------- */

.directory { padding: 28px 24px 72px; }
.directory > h1 {
  font-family: var(--mono);
  font-size: clamp(22px, 3vw, 30px);
  letter-spacing: -0.01em;
  margin: 0 0 6px;
}
.directory > p.lede { margin: 0 0 30px; color: var(--ink-2); max-width: 62ch; }

.dirsection { margin-bottom: 34px; }
.dirsection > p { margin: 0 0 12px; font-size: 13px; color: var(--ink-3); }

.toolgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(238px, 1fr));
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
}
.toolcard {
  background: var(--card);
  padding: 14px 15px 16px;
  display: flex;
  flex-direction: column;
  gap: 5px;
  text-decoration: none;
  color: inherit;
  border-left: 2px solid transparent;
}
a.toolcard:hover { border-left-color: var(--signal); }
.toolcard h3 { margin: 0; font-size: 14.5px; font-weight: 600; }
.toolcard p { margin: 0; font-size: 12.5px; color: var(--ink-3); line-height: 1.45; }
.toolcard.is-soon h3 { color: var(--ink-2); }
.toolcard .tag {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-top: auto;
  padding-top: 8px;
}
.toolcard.is-live .tag { color: var(--ok); }
.toolcard .tag.needs-server { color: var(--warn); }



/* settings: inline number field and pasted-list box */
.setting-row--inline {
  display: grid;
  grid-template-columns: 1fr 72px auto;
  align-items: center;
  gap: 8px;
}
.setting-row--inline > span:first-child { margin-bottom: 0; }
.setting-row--inline input { width: 100%; }
.setting-row--inline .suffix { font-size: 13px; color: var(--ink-3); }
.setting-row--inline .hint { grid-column: 1 / -1; }

.setting-row textarea {
  width: 100%;
  font: inherit;
  font-family: var(--mono);
  font-size: 12px;
  padding: 7px 9px;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  resize: vertical;
}

/* a setting this document has no data for */
.setting.is-unavailable label { color: var(--ink-3); cursor: not-allowed; }
.setting.is-unavailable .hint { color: var(--warn); }

/* ---------- theme toggle ---------- */

.themetoggle {
  width: 36px;
  height: 36px;
  flex: none;
  display: grid;
  place-items: center;
  padding: 0;
  cursor: pointer;
  color: var(--ink-2);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: 8px;
  transition: color 120ms ease, border-color 120ms ease;
}
.themetoggle:hover { color: var(--signal); border-color: var(--signal); }
.themetoggle svg { width: 17px; height: 17px; }

/* ---------- recent files ---------- */

.histlist { list-style: none; margin: 0; padding: 0; }
.histlist li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 0;
  border-bottom: 1px solid var(--rule-2);
}
.histlist li:last-child { border-bottom: 0; }

.histmain { min-width: 0; flex: 1; }
.histname {
  display: block;
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.histmeta {
  display: block;
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--ink-3);
  margin-top: 2px;
}
.histactions { display: flex; gap: 6px; flex: none; }

.linkish {
  font: inherit;
  font-size: inherit;
  padding: 0;
  border: 0;
  background: none;
  color: var(--signal);
  text-decoration: underline;
  cursor: pointer;
}
.linkish:hover { color: var(--ink); }

/* ---------- floating download button ---------- */

.fab {
  position: fixed;
  right: 22px;
  bottom: 22px;
  z-index: 60;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 14px 20px;
  cursor: pointer;
  color: #fff;
  background: var(--signal);
  border: 1px solid var(--signal);
  border-radius: 100px;
  box-shadow: 0 6px 16px rgba(13, 27, 42, 0.18), 0 18px 40px rgba(13, 27, 42, 0.16);
  transition: transform 140ms ease, opacity 140ms ease, background 140ms ease;
}
.fab svg { width: 17px; height: 17px; }
.fab:hover:not(.is-off) { transform: translateY(-2px); background: var(--ink); border-color: var(--ink); }

/* Present but obviously inert until the page has produced something. */
.fab.is-off {
  cursor: not-allowed;
  opacity: 0.45;
  color: var(--ink-2);
  background: var(--card);
  border-color: var(--rule);
  box-shadow: 0 2px 8px rgba(13, 27, 42, 0.1);
}

@media (max-width: 620px) {
  .fab { right: 14px; bottom: 14px; padding: 13px 17px; font-size: 11px; }
}
@media (prefers-reduced-motion: reduce) {
  .fab { transition: none; }
  .fab:hover:not(.is-off) { transform: none; }
}


/* ---------- tool header ---------- */

.toolhead {
  background: var(--card);
  border-bottom: 1px solid var(--rule);
}
/*
  One row, not three stacked blocks. The tool is what the page is for; the
  header only has to say which tool this is and let you back out. The previous
  version spent ~130px of every screen on chrome above the controls.
*/
.toolhead__inner {
  max-width: 1200px;
  margin: 0 auto;
  padding: 9px 24px 10px;
  display: flex;
  align-items: baseline;
  gap: 4px 12px;
  flex-wrap: wrap;
}

.toolhead__crumbs { font-size: 12.5px; color: var(--ink-3); flex: none; }
.toolhead__crumbs a { color: var(--ink-2); text-decoration: none; }
.toolhead__crumbs a:hover { color: var(--signal); }
.toolhead__crumbs span { margin: 0 5px; opacity: 0.55; }

.toolhead__title {
  font-family: var(--sans);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.02em;
  margin: 0;
  flex: none;
}
.toolhead__lede {
  margin: 0;
  font-size: 12.5px;
  color: var(--ink-3);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The description is the first thing to go when the row gets tight. */
@media (max-width: 720px) { .toolhead__lede { display: none; } }

/* ---------- page picker, shared by the tools that pick pages ---------- */

.pagegrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 16px;
}
.pagecard { display: flex; flex-direction: column; gap: 7px; }

.pagecard__shot {
  position: relative;
  background: #fff;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  overflow: hidden;
  line-height: 0;
}
.pagecard__shot canvas { width: 100%; height: auto; display: block; }

/* A dropped page stays on screen, greyed, so a mis-click is recoverable rather
   than a page silently disappearing from the grid. */
/* The page fades, its button does not. Dimming the whole shot took the undo
   button down with it, leaving a dropped page with no visible way back. */
.pagecard.is-out .pagecard__shot { border-style: dashed; }
.pagecard.is-out .pagecard__shot canvas { opacity: 0.3; filter: grayscale(1); }
.pagecard.is-out .pagecard__shot.is-waiting { opacity: 0.35; }
.pagecard.is-out .pagecard__cap { color: var(--ink-3); text-decoration: line-through; }

.pagecard__act {
  position: absolute;
  top: 6px; right: 6px;
  min-width: 24px; height: 24px;
  padding: 0 7px;
  display: inline-flex; align-items: center; gap: 4px;
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
  color: #fff;
  background: var(--signal);
  border: 0;
  border-radius: 100px;
  box-shadow: 0 1px 4px rgba(13, 27, 42, 0.3);
}
.pagecard__act:hover { background: var(--ink); }
.pagecard.is-out .pagecard__act { background: var(--ok); }

.pagecard__cap {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  color: var(--ink-2);
  text-align: center;
}

.pagebar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 18px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--rule-2);
}
.pagebar__count {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-right: auto;
}

/* ---------- tool page footer ---------- */

.toolfoot {
  border-top: 1px solid var(--rule);
  background: var(--card);
  /* No top margin: the tool layout ends flush against it. A gap here showed a
     band of page background between the settings rail and the footer. */
  margin-top: 0;
}
.toolfoot__inner {
  max-width: 1200px;
  margin: 0 auto;
  /* One short row now that the promise paragraph has gone to the landing page,
     so it takes the height of a line of text rather than of a paragraph. */
  padding: 10px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px 20px;
  flex-wrap: wrap;
}


.toolfoot__help {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 600;
  color: #128c4a;
  text-decoration: none;
  padding: 7px 13px;
  border: 1px solid #128c4a;
  border-radius: 100px;
  white-space: nowrap;
}
.toolfoot__help svg { width: 16px; height: 16px; }

/* Quieter than the help button beside them: these are there to be findable,
   not to compete with the one action the footer is actually offering. */
.toolfoot__legal {
  font-size: 12.5px; color: var(--ink-3); text-decoration: none;
  padding: 9px 4px;
}
.toolfoot__legal:hover { color: var(--signal); text-decoration: underline; }
.toolfoot__help:hover { background: #128c4a; color: #fff; }
:root[data-theme="dark"] .toolfoot__help { color: #4fc07f; border-color: #2f6b4a; }
:root[data-theme="dark"] .toolfoot__help:hover { background: #1c7a49; color: #fff; border-color: #1c7a49; }

/* The old strip position is retired; the placeholder can stay empty. */
#site-promise:empty { display: none; }

/* several caveats about one file, in one box */
.notice__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.notice__list li { position: relative; padding-left: 14px; line-height: 1.5; }
.notice__list li::before {
  content: "";
  position: absolute;
  left: 0; top: 8px;
  width: 4px; height: 4px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.4;
}

.proofnote {
  margin: 0 0 4px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.05em;
  color: var(--ink-3);
}

/* ---------- loading ---------- */

/* A thumbnail that has not been drawn yet: a slow sweep so a waiting grid
   reads as "working" rather than as empty boxes. */
@keyframes shimmer {
  from { background-position: -160% 0; }
  to   { background-position: 260% 0; }
}
/* `.thumb .shot` sets a white ground at two-class specificity, so the waiting
   state has to match it or the sweep is cancelled out and a pending thumbnail
   looks like a finished blank page. */
.is-waiting,
.shot.is-waiting {
  min-height: 40px;         /* holds its space so the grid does not jump */
  /* A solid ground with the sweep passing over it. Putting both stops in the
     gradient let the pale one fill the box and the placeholder read as blank
     white paper rather than as something still loading. */
  background-color: var(--rule-2);
  background-image: linear-gradient(
    100deg,
    transparent 24%,
    var(--shimmer) 42%,
    transparent 60%
  );
  background-size: 220% 100%;
  animation: shimmer 1.5s linear infinite;
}

.spinner {
  display: inline-block;
  width: 13px;
  height: 13px;
  vertical-align: -2px;
  margin-right: 7px;
  border: 2px solid var(--rule);
  border-top-color: var(--signal);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.progress-label.is-busy { color: var(--ink-2); }

@media (prefers-reduced-motion: reduce) {
  .is-waiting,
  .shot.is-waiting { animation: none; background-image: none; }
  .spinner { animation: none; border-top-color: var(--rule); }
}

/* ---------- placeholders while a file is being read ---------- */

.skel {
  height: 12px;
  border-radius: 3px;
}
.skelgroup {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding-bottom: 16px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--rule-2);
}
.skelgroup:last-child { border-bottom: 0; margin-bottom: 0; }
.skelstat {
  display: flex;
  flex-direction: column;
  gap: 8px;
  justify-content: center;
}

/* Reading status, at the top of the stage where the file was dropped. */
.stagestatus {
  display: flex;
  align-items: center;
  gap: 4px;
  margin: 0 0 20px;
  font-size: 13px;
  color: var(--ink-2);
}
.stagestatus .progress {
  flex: 1;
  min-width: 80px;
  margin-left: 10px;
}
.stagestatus.hidden { display: none; }

/* ---------- the tool on a phone: stage full screen, settings in a sheet ---------- */

.mobilebar { display: none; }
.sheet, .sheetscrim { display: none; }

/*
 * The `hidden` attribute has to win over the display rules below. A bare class
 * selector outranks the browser's own `[hidden] { display: none }`, so without
 * this the scrim stays laid out - invisible at opacity 0, inset 0, above
 * everything - and silently eats every tap on the page.
 */
.sheet[hidden], .sheetscrim[hidden] { display: none !important; }

@media (max-width: 860px) {
  /*
   * The rail is lifted out of the grid into a bottom sheet, so the stage gets
   * the whole screen. Its desktop height of 100vh with its own scrollbar is
   * what made a phone open the tool on a wall of settings that trapped the
   * scroll.
   */
  .controls {
    height: auto;
    overflow-y: visible;
    position: static;
    border-right: 0;
    border-bottom: 0;
    padding: 4px 0 0;
    background: none;
  }
  .layout { grid-template-columns: 1fr; min-height: 0; }
  .stage { padding: 16px 14px 24px; }

  /* Room for the fixed bar, plus the phone's own home indicator. */
  body.has-mobilebar { padding-bottom: calc(66px + env(safe-area-inset-bottom, 0px)); }
  body.sheet-open { overflow: hidden; }

  .mobilebar {
    display: flex;
    align-items: center;
    gap: 10px;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 70;
    padding: 10px 14px calc(10px + env(safe-area-inset-bottom, 0px));
    background: var(--card);
    border-top: 1px solid var(--rule);
  }
  .mobilebar__settings {
    flex: 1;
    min-height: 46px;
    font-family: var(--mono);
    font-size: 12px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink);
    background: none;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    cursor: pointer;
  }
  .mobilebar__settings[aria-expanded="true"] { border-color: var(--signal); color: var(--signal); }

  /* In the bar, not floating over the preview. */
  .mobilebar .fab {
    position: static;
    flex: 1;
    justify-content: center;
    min-height: 46px;
    padding: 13px 14px;
    box-shadow: none;
    transform: none;
  }
  .mobilebar__settings { flex: 0 0 auto; padding: 0 18px; }

  .sheetscrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 80;
    background: rgba(13, 27, 42, 0.45);
    opacity: 0;
    transition: opacity 0.2s ease;
  }
  .sheetscrim.is-open { opacity: 1; }

  .sheet {
    display: flex;
    flex-direction: column;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 90;
    max-height: 86vh;
    background: var(--card);
    border-top: 1px solid var(--rule);
    border-radius: 14px 14px 0 0;
    box-shadow: 0 -8px 30px rgba(13, 27, 42, 0.22);
    transform: translateY(100%);
    transition: transform 0.22s cubic-bezier(0.32, 0.72, 0, 1);
  }
  .sheet.is-open { transform: translateY(0); }

  .sheet__grab {
    flex: none;
    width: 40px;
    height: 4px;
    margin: 9px auto 3px;
    border-radius: 3px;
    background: var(--rule);
  }
  .sheet__head {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 6px 16px 10px;
    border-bottom: 1px solid var(--rule-2);
    touch-action: none;          /* so the drag-to-dismiss gesture is ours */
  }
  .sheet__title {
    margin: 0;
    font-family: var(--mono);
    font-size: 12px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-2);
  }
  .sheet__body {
    flex: 1;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 14px 16px calc(20px + env(safe-area-inset-bottom, 0px));
  }
}

@media (prefers-reduced-motion: reduce) {
  .sheet, .sheetscrim { transition: none; }
}

/* ---------- touch targets ---------- */

/*
 * Keyed on the pointer, not the width: a 24px control is too small for a thumb
 * on any size of touch screen.
 */
@media (pointer: coarse) {
  .pagecard__act { min-width: 36px; height: 36px; padding: 0 11px; font-size: 12px; }
  .themetoggle { min-width: 42px; min-height: 42px; }
  .btn--tiny { min-height: 38px; }
  .l-catsbtn, .l-navpanel a { min-height: 44px; }
}

/* ---------- image tools ---------- */

.setting-label { display: block; font-size: 14px; margin-bottom: 5px; }
.setting-hint { margin: 5px 0 0; font-size: 12px; color: var(--ink-3); line-height: 1.45; }
.setting-check {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 7px 0;
  font-size: 14px;
  cursor: pointer;
}
.setting-check input { margin-top: 2px; flex: none; }

.pagecard__shot img { width: 100%; height: auto; display: block; }
.pagecard.is-out .pagecard__shot img { opacity: 0.3; filter: grayscale(1); }
.pagecard__cap--dim { color: var(--ink-3); }

/* The one crop box, dragged on the first image and used on all of them. */
.cropstage { margin-bottom: 20px; }
.cropwrap {
  position: relative;
  display: inline-block;
  max-width: 100%;
  line-height: 0;
  cursor: crosshair;
  touch-action: none;          /* the drag gesture is ours, not the page's */
  user-select: none;
}
.cropwrap img { max-width: 100%; max-height: 420px; width: auto; display: block; }
.cropdrag {
  position: absolute;
  border: 1px solid var(--signal);
  box-shadow: 0 0 0 9999px rgba(13, 27, 42, 0.45);
  cursor: move;
}

.renamepreview { margin-bottom: 18px; }
.renamepreview__row {
  margin: 0 0 5px;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink-2);
  overflow-wrap: anywhere;
}
.renamepreview__from { color: var(--ink-3); }

/* ---------- GSTIN check ---------- */

.gstin-input {
  width: 100%;
  font-family: var(--mono);
  font-size: 19px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 14px 12px;
}
.gstin-verdict {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  margin-bottom: 18px;
  border: 1px solid var(--rule);
  border-left-width: 3px;
  border-radius: var(--radius);
  font-size: 15px;
}
.gstin-verdict.is-ok { border-left-color: var(--ok); color: var(--ok); }
.gstin-verdict.is-bad { border-left-color: var(--signal); color: var(--signal); }
.gstin-parts { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1px; }
.gstin-part { padding: 12px 14px; background: var(--card); border: 1px solid var(--rule-2); }
.gstin-part b { display: block; font-family: var(--mono); font-size: 15px; margin-bottom: 3px; }
.gstin-part span { font-size: 11.5px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-3); }
.gstin-bulk { width: 100%; min-height: 150px; font-family: var(--mono); font-size: 13px; padding: 11px; }
.gstin-row { display: flex; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--rule-2); font-size: 13px; }
.gstin-row code { font-family: var(--mono); flex: none; }
.gstin-row.is-ok code { color: var(--ok); }
.gstin-row.is-bad code { color: var(--signal); }

.toolfoot__by { margin: 0; font-size: 12.5px; color: var(--ink-3); }
.toolfoot__brand { color: var(--ink-2); font-weight: 600; text-decoration: none; }
.toolfoot__brand:hover { color: var(--signal); text-decoration: underline; }

/* ---------- the sign-in gate ---------- */

.gate {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(13, 27, 42, 0.5);
}
.gate__box {
  width: 100%;
  max-width: 380px;
  padding: 26px 24px 22px;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 10px;
  box-shadow: 0 18px 50px rgba(13, 27, 42, 0.28);
}
.gate__title { margin: 0 0 10px; font-size: 19px; }
.gate__text { margin: 0 0 16px; color: var(--ink-2); line-height: 1.55; }
.gate__note {
  margin: -6px 0 16px;
  padding: 9px 11px;
  font-size: 13px;
  color: var(--warn);
  background: var(--paper);
  border-radius: var(--radius);
}
.gate__fine { margin: 14px 0 0; font-size: 12px; color: var(--ink-3); text-align: center; }

/* ---------- the sign-in wall on a tool page ---------- */

/*
 * While we are finding out whether this browser is signed in.
 *
 * visibility, not display: the tools measure elements as they boot, and
 * display:none gives every one of them a zero-size box. This keeps the layout
 * intact and only stops it being seen - which is the whole requirement, since
 * a tool that flashes up and is then replaced reads as a fault.
 *
 * Scoped to <main> so the header stays: on a slow connection a blank page with
 * a masthead is a page that is loading, and a completely blank one is broken.
 */
/* ---- while we work out whether this visitor may be here ----
 *
 * The class is stamped onto <html> by scripts/build.js on every gated page, so
 * this stylesheet - render-blocking, in <head> - applies before the browser
 * draws anything. require-signin.js removes it once it knows.
 *
 * Adding the class from a script at the bottom of <body> was too late: by then
 * the drop zone and the settings rail had already been painted, which is the
 * flash this replaces.
 */
.pc-authcheck main { visibility: hidden; }

/*
 * The tool's furniture lives outside <main>: sheet.js moves the settings rail
 * onto document.body on a narrow screen, download-fab.js adds the floating
 * export button, and the footer carries the help link. Hiding only <main> left
 * an Export button floating over a page that had not decided yet.
 */
.pc-authcheck .fab,
.pc-authcheck .sheet,
.pc-authcheck .sheetscrim,
.pc-authcheck .mobilebar,
.pc-authcheck .toolfoot,
.pc-authcheck body > .controls { visibility: hidden; }

/*
 * A preloader, not an empty page. Nothing is drawn for a moment either way,
 * and the difference between "loading" and "broken" is entirely whether
 * something is there to say so.
 *
 * Built from body's two pseudo-elements so it needs no markup: there is no
 * element to stamp into 22 pages, nothing to remove afterwards, and it cannot
 * be left behind by a script that failed to run.
 */
.pc-authcheck body::before,
.pc-authcheck body::after {
  position: fixed;
  left: 0;
  right: 0;
  text-align: center;
  pointer-events: none;
  z-index: 40;
  /* Late enough not to flicker on a fast connection, where the answer arrives
     before anyone has registered a wait. */
  animation: pc-preload-in 260ms ease-out 220ms both,
             pc-preload-out 0s linear 6s forwards;
}

/* The mark, because it is already the thing on the tab and in the header. */
.pc-authcheck body::before {
  content: "";
  top: 50%;
  margin-top: -46px;
  height: 34px;
  background: center / 34px 34px no-repeat
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='6' y='7' width='3.5' height='18' rx='1.25' fill='%23d62246'/%3E%3Crect x='12.5' y='7' width='7' height='18' rx='1.25' fill='%23d62246'/%3E%3Crect x='23.5' y='7' width='2.5' height='18' rx='1.25' fill='%23d62246'/%3E%3C/svg%3E");
}

.pc-authcheck body::after {
  content: "Checking your account";
  top: 50%;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
}

@keyframes pc-preload-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes pc-preload-out { to { opacity: 0; visibility: hidden; } }

/* The bars read left to right, the way a scanner passes over them. */
@media (prefers-reduced-motion: no-preference) {
  .pc-authcheck body::before { animation:
      pc-preload-in 260ms ease-out 220ms both,
      pc-scan 1100ms ease-in-out 480ms infinite,
      pc-preload-out 0s linear 6s forwards; }
}
@keyframes pc-scan {
  0%, 100% { opacity: 0.45; }
  50%      { opacity: 1; }
}

/*
 * Never leave a page hidden because a script did not arrive.
 *
 * Hiding by default trades a visible flash for a possible blank page: if
 * require-signin.js is blocked by an extension, or a deploy is half-done, the
 * class is never removed and the tool is invisible forever. These animations
 * are the failsafe - they run without JavaScript and reveal the page six
 * seconds in. A working gate has decided long before that, so this only fires
 * when the gate did not run at all.
 *
 * Showing a tool to someone who has not signed in is a business problem. A
 * blank page is a broken product, which is worse, and the server-side check is
 * what should be relied on for the first one regardless.
 */
@keyframes pc-authcheck-failsafe { to { visibility: visible; } }
.pc-authcheck main,
.pc-authcheck .fab,
.pc-authcheck .sheet,
.pc-authcheck .mobilebar,
.pc-authcheck .toolfoot,
.pc-authcheck body > .controls { animation: pc-authcheck-failsafe 0s linear 6s forwards; }

/*
 * With JavaScript off entirely, none of this will ever run. The tools need it
 * anyway, so the honest thing is to show the page and let it say so.
 */
@media (scripting: none) {
  .pc-authcheck main, .pc-authcheck .fab, .pc-authcheck .sheet,
  .pc-authcheck .mobilebar, .pc-authcheck .toolfoot,
  .pc-authcheck body > .controls { visibility: visible; animation: none; }
  .pc-authcheck body::before, .pc-authcheck body::after { content: none; }
}

/* ---- the stage: what went in, and what comes out ----
   The grid on the left is the file; the panel on the right is the result of
   the current settings, composed by the real exporter. Before this, every
   setting below Cropping changed something nobody could see. */
.stagesplit { display: grid; grid-template-columns: 1fr 380px; gap: 22px; align-items: start; }
.stagesplit__title {
  margin: 0 0 10px; font-family: var(--mono); font-size: 10.5px;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-3);
}
.stagesplit__out {
  position: sticky; top: 16px;
  background: var(--card); border: 1px solid var(--rule);
  border-radius: var(--radius);
  /* Room at the foot for the floating export button, which is fixed to the
     bottom-right and otherwise sits on top of the last page shown here. */
  padding: 14px 14px 76px;
}
.result__note { margin: 0 0 12px; font-size: 12px; color: var(--ink-3); line-height: 1.5; }
.result__page { margin: 0 0 14px; }
.result__page canvas {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--rule); border-radius: 2px; background: #fff;
}
.result__page figcaption {
  margin-top: 5px; font-family: var(--mono); font-size: 10px;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-3);
}

/* On a narrow screen the result comes FIRST: it is the answer to the question
   someone is actually asking while they change a setting. */
@media (max-width: 860px) {
  .stagesplit { grid-template-columns: 1fr; }
  .stagesplit__out { position: static; order: -1; }
}
