/* FileConverter, converter shell */

:root {
  --bg: #f3f7fd;
  --surface: #ffffff;
  --glass: rgba(255, 255, 255, .64);
  --glass-edge: rgba(37, 99, 235, .10);
  --frost-a: rgba(59, 130, 246, .11);
  --frost-b: rgba(14, 165, 233, .08);
  --frost-c: rgba(99, 102, 241, .07);
  --surface-2: #f1f5f9;
  --border: #e2e8f0;
  --text: #0f172a;
  --muted: #64748b;
  --accent: #2563eb;
  --accent-ink: #ffffff;
  --accent-soft: #eff6ff;
  --ok: #059669;
  --warn: #b45309;
  --warn-soft: #fffbeb;
  --danger: #dc2626;
  --danger-soft: #fef2f2;
  --radius: 10px;
  --shadow: 0 1px 2px rgba(15, 23, 42, .06), 0 8px 24px rgba(15, 23, 42, .08);
  --topbar-h: 56px;
}

/* Dark mode is NEUTRAL, not navy.
 *
 * It used to be #0b1120 on #111827 - the blue-black every framework ships as
 * its default dark palette. It is instantly recognisable as a default, and on
 * a site whose whole job is other people's artwork it also tints every
 * preview: a grey logo on a navy page reads warm, and a white one reads cold.
 * These greys are hue-free, so what is on the canvas is the only colour on
 * screen. The accent stays blue, because it is the one thing that should be
 * coloured.
 *
 * Kept in one block and repeated for the explicit override, because the media
 * query serves people who never touch the toggle and the attribute serves
 * people who did. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #131314;
    --surface: #1b1b1d;
    --glass: rgba(27, 27, 29, .72);
    --glass-edge: rgba(77, 141, 246, .12);
    --frost-a: rgba(77, 141, 246, .085);
    --frost-b: rgba(56, 189, 248, .05);
    --frost-c: rgba(99, 102, 241, .05);
    --surface-2: #232326;
    --border: #3a3a41;
    --text: #ededee;
    --muted: #a0a0a6;
    --accent: #4d8df6;
    /* Dark ink ON the accent, not white. The accent has to be bright enough
       to read against a dark page, and once it is that bright, white text on
       top of it lands at 3.2:1. Flipping the ink dark gives 5.8:1 and keeps
       the accent where it needs to be. */
    --accent-ink: #10131a;
    --accent-soft: #1d2430;
    --ok: #34d399;
    --warn: #fbbf24;
    --warn-soft: #2a2415;
    --danger: #f87171;
    --danger-soft: #2b1a1a;
    --shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 8px 24px rgba(0, 0, 0, .5);
  }
}
:root[data-theme="dark"] {
  --bg: #131314; --surface: #1b1b1d; --glass: rgba(27, 27, 29, .72); --glass-edge: rgba(77, 141, 246, .12);
  --frost-a: rgba(77, 141, 246, .085); --frost-b: rgba(56, 189, 248, .05); --frost-c: rgba(99, 102, 241, .05); --surface-2: #232326; --border: #3a3a41;
  --text: #ededee; --muted: #a0a0a6; --accent: #4d8df6; --accent-ink: #10131a; --accent-soft: #1d2430;
  --ok: #34d399; --warn: #fbbf24; --warn-soft: #2a2415;
  --danger: #f87171; --danger-soft: #2b1a1a;
  --shadow: 0 1px 2px rgba(0,0,0,.5), 0 8px 24px rgba(0,0,0,.5);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* ---------- Frosted page background ----------
 * A very faint blue glass behind every page: three soft pools of blue on the
 * page tint, with a fine grain over them so it reads as frosted rather than
 * as a flat gradient. It sits in a fixed layer behind everything, so it does
 * not scroll, repaint with content, or tint the canvases and previews, which
 * keep their own solid backgrounds. Cards, the top bar and the footer are
 * translucent over it; controls and work areas stay solid. */
body::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .5 0 0 0 0 .6 0 0 0 0 .8 0 0 0 .05 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E"),
    radial-gradient(48% 42% at 8% 4%, var(--frost-a), transparent 72%),
    radial-gradient(40% 36% at 94% 14%, var(--frost-b), transparent 72%),
    radial-gradient(55% 45% at 72% 100%, var(--frost-c), transparent 72%),
    radial-gradient(38% 34% at 20% 78%, var(--frost-b), transparent 72%);
}

/* The blur lives on a pseudo-element, not on the bar itself: an element with
   backdrop-filter becomes the containing block for its fixed-position
   children, and the preview dock inside the top bar is a fixed bottom sheet
   on a phone. Put the filter on the bar and that sheet anchors to the bar
   instead of the screen. */
.topbar, .site-foot {
  background: transparent !important;
  border-color: var(--glass-edge) !important;
  isolation: isolate;
}
.site-foot { position: relative; }
.topbar::before, .site-foot::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: var(--glass);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  backdrop-filter: blur(14px) saturate(1.3);
}
.convcard, .flowcard, .whycard, .howsteps li, .post-card, .related, .tile, .plan, .stat-cell {
  background: var(--glass);
  border-color: var(--glass-edge);
}

/* One rule, instead of the eight that were here.
 *
 * A class that sets `display` beats the browser's own `[hidden]` rule,
 * because a class selector outranks an attribute one in the UA sheet. So any
 * element styled with `display: grid` or `flex` stays on screen while
 * carrying the attribute that is supposed to hide it. This had been patched
 * seven times, once per element that hit it - nav badge, preview dock, nav
 * menu, search results, editor stage, photo panes, viewer panes - and the
 * eighth was the converter work area, which meant the settings panel sat
 * there fully visible before anyone had chosen a file.
 *
 * !important is the point rather than a shortcut: `hidden` is the author
 * saying this element is not on the page, and nothing should quietly outrank
 * that.
 */
[hidden] { display: none !important; }

/* ---------- Top bar ---------- */

.topbar {
  position: sticky; top: 0; z-index: 50;
  height: var(--topbar-h);
  display: flex; align-items: center; gap: 8px;
  padding: 0 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.brand {
  display: flex; align-items: center;
  margin-right: 8px; flex: none;
}
.brand .logo { display: block; height: 24px; width: auto; }

/* The logo ships in two colourings: blue and slate for light pages, a
   lighter blue and pale grey for dark ones. Both are in the markup and CSS
   shows the one that matches the theme, so there is no flash when the page
   loads and no script involved. */
.logo-d { display: none !important; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .logo-l { display: none !important; }
  :root:not([data-theme="light"]) .logo-d { display: block !important; }
}
:root[data-theme="dark"] .logo-l { display: none !important; }
:root[data-theme="dark"] .logo-d { display: block !important; }

/* ---------- Hero wordmark ----------
 * The product name, said once, large, above the page heading. The top bar
 * carries it at nav size where it reads as a back-link; this is the version
 * that tells someone which site they have landed on. */
.wordmark {
  display: flex; justify-content: center;
  margin: 4px 0 10px;
}
.wordmark .logo { height: clamp(34px, 6.5vw, 56px); width: auto; max-width: 100%; }
.wordmark-sub {
  text-align: center; margin: 0 0 18px;
  color: var(--muted); font-size: 14px;
}

.nav { display: flex; align-items: center; gap: 2px; flex: 1; min-width: 0; }

.nav-anchor {
  flex: none;
  position: relative;   /* positioning context for the docked preview panel */
  display: inline-flex;
}

.nav-item {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 11px; border-radius: 8px;
  background: none; border: 1px solid transparent;
  color: var(--muted); font: inherit; font-size: 14px; font-weight: 500;
  cursor: pointer; white-space: nowrap;
}
.nav-item:hover { background: var(--surface-2); color: var(--text); }
.nav-item[aria-expanded="true"],
.nav-item.is-active {
  background: var(--accent-soft); color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 25%, transparent);
}
.nav-item:disabled { opacity: .45; cursor: not-allowed; }

.nav-badge {
  min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: 999px; background: var(--accent); color: var(--accent-ink);
  font-size: 11px; font-weight: 700; display: grid; place-items: center;
}

/* ---------- Preview panel: docked to its top-bar item ---------- */

.preview-dock {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  width: min(460px, calc(100vw - 32px));
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  text-align: left;
  cursor: default;
}
/* Keep the panel on screen when its trigger sits near the right edge. */
.preview-dock.align-right { left: auto; right: 0; }

.dock-head {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 12px; border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
.dock-title { font-size: 13px; font-weight: 650; flex: 1; }
.dock-close {
  background: none; border: none; color: var(--muted);
  font-size: 18px; line-height: 1; cursor: pointer; padding: 2px 6px; border-radius: 6px;
}
.dock-close:hover { background: var(--border); color: var(--text); }

.dock-tabs {
  display: flex; gap: 2px; padding: 6px 8px;
  border-bottom: 1px solid var(--border); overflow-x: auto;
}
.dock-tab {
  padding: 5px 9px; border-radius: 6px; border: 1px solid transparent;
  background: none; color: var(--muted);
  font: inherit; font-size: 12px; font-weight: 600; cursor: pointer; white-space: nowrap;
}
.dock-tab:hover { background: var(--surface-2); color: var(--text); }
.dock-tab.is-active {
  background: var(--accent); color: var(--accent-ink); border-color: var(--accent);
}

.dock-stage {
  position: relative;
  height: 260px;
  background:
    linear-gradient(45deg, var(--surface-2) 25%, transparent 25%) 0 0/16px 16px,
    linear-gradient(-45deg, var(--surface-2) 25%, transparent 25%) 0 8px/16px 16px,
    linear-gradient(45deg, transparent 75%, var(--surface-2) 75%) 8px -8px/16px 16px,
    linear-gradient(-45deg, transparent 75%, var(--surface-2) 75%) -8px 0/16px 16px;
  display: grid; place-items: center; overflow: hidden;
}
.dock-stage > * { max-width: 100%; max-height: 100%; }
.dock-stage svg, .dock-stage img, .dock-stage canvas { display: block; object-fit: contain; }
.dock-stage pre {
  margin: 0; padding: 10px; width: 100%; height: 100%; overflow: auto;
  font: 11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--surface); color: var(--text); text-align: left;
  white-space: pre; align-self: stretch; justify-self: stretch;
}

.dock-meta {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 12px; border-top: 1px solid var(--border);
  font-size: 12px; color: var(--muted);
}
.dock-meta strong { color: var(--text); font-weight: 650; }
.dock-actions { display: flex; gap: 6px; padding: 8px 12px; border-top: 1px solid var(--border); }

/* ---------- Layout ---------- */

.wrap { max-width: 1180px; margin: 0 auto; padding: 24px 16px 72px; }
.hero { text-align: center; margin: 12px 0 28px; }
.hero h1 { font-size: clamp(26px, 4vw, 38px); line-height: 1.15; margin: 0 0 8px; letter-spacing: -.02em; }
.hero p { color: var(--muted); margin: 0 auto; max-width: 56ch; }

.cols { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); gap: 20px; align-items: start; }
@media (max-width: 900px) { .cols { grid-template-columns: 1fr; } }

/* A tool page is the converter, its settings, and then the reading matter.
 *
 * Stacked in source order the settings landed after How it works, Why it
 * matters, the FAQ and the related links - so on any screen under 900px you
 * dropped a file and then scrolled past four cards of prose to reach the one
 * control you needed. Named areas put the settings directly under the
 * dropzone there, and keep them beside it on a wide screen. */
/* The first screen is the heading and the dropzone, and nothing else.
   Settings used to sit beside the upload before there was a file to apply
   them to: a tracing mode, a laser feed rate and a vinyl overcut, all shown
   to somebody who had arrived from a search and had not yet chosen a file.
   The work area below is revealed once one has. */
.hero-tool { text-align: center; max-width: 60rem; margin: 0 auto 6px; }
.hero-tool h1 { margin: 6px 0 8px; }
.hero-tool .hero-sub { color: var(--muted); max-width: 46rem; margin: 0 auto; }

.tool-intake { max-width: 58rem; margin: 0 auto 26px; }
.tool-intake .drop-card { padding: 10px; }
.tool-intake .drop { min-height: 260px; }
.tool-intake .drop strong { font-size: 19px; }
.tool-intake .drop-works,
.tool-intake .drop-multi { text-align: center; }

.trust {
  list-style: none; margin: 14px 0 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 8px 22px;
  color: var(--muted); font-size: 13.5px;
}
.trust li { display: flex; align-items: center; gap: 7px; }
.trust li::before {
  content: ''; width: 13px; height: 8px; flex: 0 0 auto;
  border-left: 2px solid var(--ok, #15803d); border-bottom: 2px solid var(--ok, #15803d);
  transform: rotate(-45deg) translate(1px, -2px);
}

.cols-prose { grid-template-columns: minmax(0, 1fr); grid-template-areas: "prose"; }
/* (Without the area named here, .col-prose-body's grid-area: prose - meant
   for the tool layout - made the browser invent columns for it, sized to its
   text, and every "How it works" card ran 800px wide off a phone screen.) */

/* ---------- Portal ---------- */

/* A sidebar and a panel, not fourteen cards stacked.
   The sidebar is what makes this a workspace rather than a settings page:
   the balance is always in view, and the sections are grouped by what
   somebody is doing rather than by what the database calls them. */
.portal {
  display: grid; grid-template-columns: var(--pside-w, 248px) minmax(0, 1fr);
  gap: 20px; max-width: var(--wrap, 1240px); margin: 0 auto; padding: 18px 16px 40px;
}
@media (max-width: 900px) { .portal { grid-template-columns: minmax(0, 1fr); } }

.pside { display: grid; gap: 14px; align-content: start; }
/* 72px, not 64: the widest balance worth showing (12500.99) needs 61px of
   digits, and a rail that ellipsises money is worse than one that is 8px
   wider. Still 176px back from the 248px column. */
.portal:has(.pside.is-collapsed) { --pside-w: 72px; }
@media (min-width: 901px) {
  .pside { position: sticky; top: calc(var(--topbar-h) + 12px); }
}

/* Centred: it is one number and the thing you do about it. A figure that
   size reads as a status, and a status lines up better than it ranges left. */
.pcredits {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); padding: 14px; display: grid; gap: 4px;
  justify-items: center; text-align: center;
}
.pcredits-label {
  font-size: 11px; text-transform: uppercase; letter-spacing: .07em;
  color: var(--muted); font-weight: 700;
  display: inline-flex; align-items: center; gap: 5px;
}
.pcredits-label .icon { opacity: .75; }
.pcredits b { font-size: 30px; line-height: 1.1; }
.pcredits-plan { font-size: 12.5px; color: var(--muted); margin-bottom: 8px; }
.pcredits-top {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
}

.pnav { display: grid; gap: 14px; }
.pnav-group { display: grid; gap: 2px; }
.pnav-label {
  font-size: 11px; text-transform: uppercase; letter-spacing: .07em;
  color: var(--muted); font-weight: 700; padding: 0 10px 4px;
}
.pnav-item {
  display: flex; align-items: center; gap: 10px;
  width: 100%; text-align: left;
  padding: 9px 10px; border: 0; border-radius: 8px;
  background: none; color: var(--text); cursor: pointer;
  font: inherit; font-size: 14px; min-height: 40px;
}
/* The icon sits on its own tinted square, so the column of shapes reads as
   a column rather than as ragged glyphs beside the words. */
.pnav-ico {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; flex: 0 0 28px;
  border-radius: 7px; background: var(--surface-2); color: var(--muted);
}
.pnav-item:hover .pnav-ico { color: var(--text); }
.pnav-item.is-active .pnav-ico { background: var(--accent); color: #fff; }
.pnav-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pnav-item:hover { background: var(--surface-2); }
.pnav-item.is-active { background: var(--accent-soft); color: var(--accent); font-weight: 650; }
.pnav-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ------------------------------------------------ the collapsed sidebar */
/*
 * Narrowed, the labels go and the icons stay. The column itself shrinks, so
 * the work beside it gets the width back - which is the only reason anybody
 * collapses a sidebar.
 */
.pside-toggle {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 9px 10px; min-height: 40px;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--muted);
  font: inherit; font-size: 13px; cursor: pointer;
}
.pside-toggle:hover { background: var(--surface-2); color: var(--text); }
.pside-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.pside-toggle .icon { flex: 0 0 auto; }

.pside.is-collapsed .pnav-text,
.pside.is-collapsed .pcredits-label,
.pside.is-collapsed .pcredits-plan,
.pside.is-collapsed .pnav-label { display: none; }

.pside.is-collapsed .pnav-item,
.pside.is-collapsed .pside-toggle { justify-content: center; padding: 9px 0; gap: 0; }

/* The balance is the one thing worth keeping when the words go: it is why
   the card is there, and it still fits. */
/* The rail is 64px and the card has to live inside it. Left to size itself
   the balance widened the card past the rail - 83px at 125.44, 109px at
   12500.99 - and it pushed out over the page beside it. */
.pside { min-width: 0; }
.pside.is-collapsed .pcredits {
  padding: 10px 4px;
  width: 100%; max-width: 100%; overflow: hidden;
}
.pside.is-collapsed .pcredits b {
  font-size: 12px; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pside.is-collapsed .pcredits .btn { padding: 7px 0; width: 100%; }

/* Groups keep their spacing without their headings, so the icons stay in
   the same runs they read as when expanded. */
.pside.is-collapsed .pnav-group { gap: 4px; }
.pside.is-collapsed .pside-toggle .icon { transform: scaleX(-1); }

.pmain { min-width: 0; }
.ppanel {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 18px; margin-bottom: 16px;
}
.ppanel > h2 { font-size: 18px; text-transform: none; letter-spacing: 0;
               color: var(--text); margin: 0 0 12px; }
.ppanel .tiles { margin-top: 12px; }

.admin-gate { grid-column: 1 / -1; max-width: 44rem; margin: 0 auto; }
.admin-who { font-size: 15px; line-height: 1.3; word-break: break-word; }

/* One question and its answer, as a block, so a long answer does not squeeze
   the question into a column two words wide. */
.faq-edit {
  display: grid; gap: 6px; margin-bottom: 12px; padding: 12px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2);
}
.faq-edit .faq-q { font-weight: 600; }
.faq-edit textarea { width: 100%; font: inherit; resize: vertical; }
.faq-edit button { justify-self: start; }
.hint.is-error { color: var(--err-ink, #b91c1c); }

/* The marketing blocks on a route page.
   Bold inside prose is doing a job here: a page of unbroken grey gives a
   reader nothing to land on and a search engine nothing to weight. */
.hero-meta { font-size: 12.5px; color: var(--muted); margin-top: 8px; }
.benefits p strong, .faq p strong, .upsell strong { color: var(--text); font-weight: 650; }

.upsell { border-left: 3px solid var(--accent); }
.upsell .hint { font-style: italic; }

.linkfarm {
  display: grid; gap: 18px;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
}
.linkcol h3 { font-size: 12px; text-transform: uppercase; letter-spacing: .06em;
              color: var(--muted); margin: 0 0 8px; }
.linkcol ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 5px; }
.linkcol a { font-size: 13.5px; }

.cta-card { text-align: center; }
.cta-card h2 { font-size: 20px; text-transform: none; letter-spacing: 0; color: var(--text); }
.btn-lg { padding: 13px 26px; font-size: 16px; }

/* ---------- Landing page sections ---------- */

.home-section { margin: 42px auto; max-width: var(--wrap, 1240px); text-align: center; }
.home-section > h2 {
  font-size: 27px; margin: 6px 0 8px; text-transform: none;
  letter-spacing: -.01em; color: var(--text);
}
.eyebrow {
  display: inline-block; font-size: 11.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .1em; color: var(--accent);
}
.section-sub { color: var(--muted); max-width: 46rem; margin: 0 auto 20px; }
.section-more { margin-top: 16px; font-size: 14px; }

.stats-band {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px; margin: 34px auto 18px; max-width: var(--wrap, 1240px);
}
.stat-cell {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); padding: 16px; text-align: center;
}
.stat-cell b { display: block; font-size: 28px; line-height: 1.1; }
.stat-cell span { display: block; margin-top: 4px; font-size: 12.5px; color: var(--muted); }

.pill-row { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; margin: 0 0 10px; }
.pill {
  padding: 6px 14px; border-radius: 999px; font-size: 13px; font-weight: 600;
  border: 1px solid var(--border); background: var(--surface); color: var(--muted);
}
.pill-ok { border-color: var(--ok, #15803d); color: var(--ok, #15803d); }
.pill-info { border-color: var(--accent); color: var(--accent); }
.trusted { text-align: center; color: var(--muted); font-size: 13.5px; margin: 0 0 8px; }
.trusted b { color: var(--text); font-weight: 600; }

/* The conversion grid. This is the biggest internal-linking surface on the
   site: it is how three hundred small pages get crawled at all. */
.convgrid {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  text-align: left;
}
.convcard {
  display: grid; gap: 8px; align-content: start;
  padding: 14px; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: inherit; text-decoration: none;
  transition: border-color .15s, transform .15s;
}
.convcard:hover { border-color: var(--accent); transform: translateY(-1px); }
.convcard b { font-size: 14px; line-height: 1.35; font-weight: 600; }
.convpair { display: flex; align-items: center; gap: 6px; }
.convpair i {
  font-style: normal; font-size: 10.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em;
  padding: 3px 7px; border-radius: 5px;
}
.convpair .cf { background: var(--accent-soft); color: var(--accent); }
.convpair .ct { background: var(--ok-soft, #dcfce7); color: var(--ok, #15803d); }
.convpair .carr { color: var(--muted); padding: 0; font-size: 14px; }

.flowgrid {
  display: grid; gap: 12px; text-align: left;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.flowcard {
  display: grid; gap: 6px; align-content: start; padding: 18px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: inherit; text-decoration: none;
  transition: border-color .15s;
}
.flowcard:hover { border-color: var(--accent); }
.flowcard b { font-size: 15.5px; }
.flowcard span { color: var(--muted); font-size: 13.5px; line-height: 1.5; }

.whygrid {
  display: grid; gap: 12px; text-align: left;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
.whycard {
  padding: 18px; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface);
}
.whycard b { display: block; margin-bottom: 6px; font-size: 15.5px; }
.whycard p { margin: 0; color: var(--muted); font-size: 13.5px; line-height: 1.55; }

.howsteps {
  counter-reset: step; list-style: none; margin: 0; padding: 0;
  display: grid; gap: 12px; text-align: left;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.howsteps li {
  counter-increment: step; position: relative;
  padding: 18px 18px 18px 54px;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
}
.howsteps li::before {
  content: counter(step); position: absolute; left: 18px; top: 18px;
  width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--accent); color: var(--accent-ink); font-size: 13px; font-weight: 700;
}
.howsteps b { display: block; margin-bottom: 4px; }
.howsteps span { color: var(--muted); font-size: 13.5px; line-height: 1.55; }

.plan-teaser {
  border: 2px solid var(--accent); border-radius: var(--radius);
  background: var(--accent-soft); padding: 28px; max-width: 42rem; margin: 0 auto;
}
.plan-teaser h2 { font-size: 34px; margin: 10px 0 8px; text-transform: none; letter-spacing: -.02em; color: var(--text); }
.plan-teaser p { color: var(--muted); max-width: 34rem; margin: 0 auto 16px; }

@media (max-width: 700px) {
  .home-section { margin: 32px auto; }
  .home-section > h2 { font-size: 22px; }
  .stat-cell b { font-size: 23px; }
  .plan-teaser { padding: 20px; }
  .plan-teaser h2 { font-size: 28px; }
}

/* ---------- Blog ---------- */

.post-grid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.post-card {
  display: grid; gap: 6px; align-content: start;
  padding: 16px; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: inherit; text-decoration: none;
  transition: border-color .15s, transform .15s;
}
.post-card:hover { border-color: var(--accent); transform: translateY(-1px); }
.post-card b { font-size: 15.5px; line-height: 1.35; }
.post-lead { color: var(--muted); font-size: 13.5px; line-height: 1.5; }
.post-meta { color: var(--muted); font-size: 12.5px; }

.post { max-width: 46rem; margin: 0 auto; }
.post-head { margin-bottom: 20px; }
.post-head h1 { margin: 6px 0 8px; }
.post-standfirst { font-size: 17px; line-height: 1.55; color: var(--text); }
.post-tag {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  background: var(--surface-2); font-size: 11.5px;
}
/* A column of text, at a width that can actually be read: the long lines a
   full-width container gives you are why people bounce off an article. */
.post-body { font-size: 16px; line-height: 1.65; }
.post-body h2 { font-size: 19px; margin: 28px 0 10px; text-transform: none;
                letter-spacing: 0; color: var(--text); }
.post-body p { margin: 0 0 14px; }
.post-body .note { margin: 18px 0; }
.post-body .cmp-table { min-width: 440px; font-size: 13.5px; }
.post-list { margin: 0 0 14px; padding-left: 20px; }
.post-list li { margin-bottom: 7px; }
.post-foot { margin-top: 28px; }

/* The comparison table. It is the point of that page, so it gets to be wide
   and to scroll sideways on a phone rather than reflow into nonsense. */
.cmp-card { padding: 0; overflow: hidden; }
.cmp-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.cmp-card > .hint { padding: 0 16px 14px; margin: 10px 0 0; }
.cmp-table { width: 100%; border-collapse: collapse; font-size: 14px; min-width: 760px; }
.cmp-table th, .cmp-table td {
  padding: 12px 14px; text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--border);
}
.cmp-table thead th {
  font-size: 12px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); white-space: nowrap;
  position: sticky; top: 0; background: var(--surface); z-index: 1;
}
.cmp-table tbody th { font-weight: 600; color: var(--muted); width: 22%; }
.cmp-table td { color: var(--muted); }
.cmp-table .cmp-us { background: var(--accent-soft); color: var(--text); }
.cmp-table thead .cmp-us { color: var(--accent); font-weight: 700; }
.cmp-table tbody tr:last-child th,
.cmp-table tbody tr:last-child td { border-bottom: 0; }

.cols-tool {
  grid-template-areas: "tool side" "prose side";
}
.col-tool { grid-area: tool; }
.col-side { grid-area: side; }
.col-prose-body { grid-area: prose; }

/* Pinned on a tall screen, so the settings stay reachable while the preview
 * and the reading matter scroll. Not pinned when the panel is taller than the
 * viewport, or it would trap its own overflow. */
@media (min-width: 901px) {
  .col-side {
    position: sticky;
    top: calc(var(--topbar-h) + 12px);
    max-height: calc(100vh - var(--topbar-h) - 24px);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
}
@media (max-width: 900px) {
  .cols-tool { grid-template-areas: "tool" "side" "prose"; }
}

.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 16px; margin-bottom: 16px;
}
.card h2 { font-size: 14px; margin: 0 0 12px; letter-spacing: .02em; text-transform: uppercase; color: var(--muted); }

/* ---------- Controls ---------- */

.field { margin-bottom: 12px; }
.field label { display: block; font-size: 12px; font-weight: 600; color: var(--muted); margin-bottom: 5px; }
.field-row { display: flex; gap: 8px; }
.field-row > * { flex: 1; min-width: 0; }

input[type="number"], input[type="text"], select {
  width: 100%; padding: 7px 9px;
  border: 1px solid var(--border); border-radius: 7px;
  background: var(--surface); color: var(--text); font: inherit; font-size: 14px;
}
input[type="range"] { width: 100%; accent-color: var(--accent); }
.range-val { float: right; font-variant-numeric: tabular-nums; color: var(--text); font-weight: 600; }

.seg { display: flex; gap: 2px; background: var(--surface-2); padding: 3px; border-radius: 8px; }
.seg button {
  flex: 1; padding: 6px 8px; border: none; border-radius: 6px;
  background: none; color: var(--muted); font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
}
.seg button.is-active { background: var(--surface); color: var(--text); box-shadow: 0 1px 2px rgba(0,0,0,.08); }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 9px 14px; border-radius: 8px; border: 1px solid var(--border);
  background: var(--surface); color: var(--text);
  font: inherit; font-size: 14px; font-weight: 600; cursor: pointer;
}
.btn:hover:not(:disabled) { background: var(--surface-2); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.btn-primary:hover:not(:disabled) { filter: brightness(1.07); background: var(--accent); }
.btn-sm { padding: 5px 9px; font-size: 12px; }
.btn-block { width: 100%; }

/* ---------- Export grid ---------- */

.exports { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; }
.export {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  padding: 10px 11px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); cursor: pointer; text-align: left; font: inherit;
  color: var(--text);
}
.export:hover:not(:disabled) { border-color: var(--accent); background: var(--accent-soft); }
.export:disabled { opacity: .45; cursor: not-allowed; }
.export b { font-size: 13px; font-weight: 700; }
.export span { font-size: 11px; color: var(--muted); }

/* ---------- Status / messages ---------- */

.note { padding: 9px 11px; border-radius: 8px; font-size: 13px; margin-bottom: 10px; }
.note-warn { background: var(--warn-soft); color: var(--warn); border: 1px solid color-mix(in srgb, var(--warn) 25%, transparent); }
.note-error { background: var(--danger-soft); color: var(--danger); border: 1px solid color-mix(in srgb, var(--danger) 25%, transparent); }
.note-info { background: var(--accent-soft); color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent); }

.stats { display: flex; flex-wrap: wrap; gap: 10px 18px; font-size: 12px; color: var(--muted); }
.stats b { color: var(--text); font-variant-numeric: tabular-nums; }

.spinner {
  width: 15px; height: 15px; border: 2px solid var(--border);
  border-top-color: var(--accent); border-radius: 50%;
  animation: spin .7s linear infinite; display: inline-block;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation-duration: 2.4s; } }

.hint { font-size: 12px; color: var(--muted); margin: 6px 0 0; }

/* ---------- Mockup ---------- */

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

/* ---------- Generated page elements ---------- */

a.brand { text-decoration: none; color: inherit; }
a.nav-item { text-decoration: none; display: inline-flex; align-items: center; }

.crumb { font-size: 13px; color: var(--muted); margin: 0 0 16px; }
.crumb a { color: var(--muted); }
.crumb a:hover { color: var(--accent); }

/* The category line above the heading. It was a tinted capsule, which is the
 * same decoration-instead-of-information the mode line had. Set as small caps
 * it still reads as a category and stops competing with the H1 under it. */
.badge {
  display: inline-block; margin-bottom: 8px;
  color: var(--muted);
  font-size: 11.5px; font-weight: 650;
  letter-spacing: .09em; text-transform: uppercase;
}

.works-with { font-size: 13px; color: var(--muted); margin-top: 10px; }

.steps { list-style: none; counter-reset: s; padding: 0; margin: 0; }
.steps li {
  counter-increment: s; position: relative;
  padding-left: 40px; margin-bottom: 16px;
}
.steps li::before {
  content: counter(s);
  position: absolute; left: 0; top: 0;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--accent); color: var(--accent-ink);
  display: grid; place-items: center; font-size: 13px; font-weight: 700;
}
.steps h3, .benefits h3 { font-size: 14px; margin: 0 0 4px; }
.steps p, .benefits p { margin: 0; color: var(--muted); font-size: 14px; }

.benefits { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px; }

.faq details {
  border-bottom: 1px solid var(--border); padding: 10px 0;
}
.faq details:last-child { border-bottom: none; }
.faq summary { cursor: pointer; font-weight: 600; font-size: 14px; list-style: none; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::before { content: '+ '; color: var(--accent); font-weight: 700; }
.faq details[open] summary::before { content: '\2212 '; }
.faq p { margin: 8px 0 0; color: var(--muted); font-size: 14px; }

.related {
  font-size: 13px; color: var(--muted); line-height: 2;
  padding: 14px 16px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius);
}
.related a { color: var(--accent); text-decoration: none; }
.related a:hover { text-decoration: underline; }

.group { margin-bottom: 32px; }
.group h2 { font-size: 16px; margin: 0 0 4px; }
.group .hint { margin: 0 0 12px; }

.tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 8px; }
.tile {
  display: flex; flex-direction: column; gap: 2px;
  padding: 11px 13px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); text-decoration: none; color: var(--text);
}
.tile:hover { border-color: var(--accent); background: var(--accent-soft); }
.tile b { font-size: 13px; }
.tile span { font-size: 11px; color: var(--muted); }
.tile.is-planned { opacity: .62; }

.spec dl { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; margin: 0 0 10px; }
.spec dt { font-size: 12px; color: var(--muted); font-weight: 600; margin-bottom: 2px; }
.spec dd { margin: 0; font-size: 14px; font-weight: 600; }

.plans { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 16px; margin-bottom: 16px; }
.plan { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px; }
.plan h3 { margin: 0 0 8px; font-size: 15px; }
.plan .price { font-size: 30px; font-weight: 700; margin: 0; letter-spacing: -.02em; }
.plan .price span { font-size: 14px; font-weight: 500; color: var(--muted); }
.plan .plan-vol { margin: 10px 0; font-size: 14px; }
.plan ul { margin: 10px 0 0; padding-left: 18px; font-size: 13px; color: var(--muted); }
.plan li { margin-bottom: 4px; }

.site-foot {
  border-top: 1px solid var(--border); margin-top: 48px;
  padding: 24px 0; font-size: 13px; color: var(--muted);
}
.site-foot p { margin: 0 0 6px; }
.site-foot a { color: var(--accent); text-decoration: none; }

/* ---------- Nav dropdown ---------- */

.nav-anchor.has-menu { position: relative; }
.nav-item .caret { font-size: 9px; opacity: .7; }

.nav-menu {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 60;
  min-width: 220px; padding: 6px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow);
  display: flex; flex-direction: column; gap: 1px;
}
.nav-menu a {
  padding: 7px 10px; border-radius: 6px;
  color: var(--text); text-decoration: none; font-size: 14px; white-space: nowrap;
}
.nav-menu a:hover { background: var(--surface-2); color: var(--accent); }

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

.site-foot {
  border-top: 1px solid var(--border); margin-top: 56px;
  padding: 32px 0 24px; background: var(--surface);
}
.foot-top { margin-bottom: 26px; max-width: 46ch; }
.foot-top .brand { margin-bottom: 8px; }
.foot-top p { margin: 0; font-size: 13px; color: var(--muted); }

.foot-cols {
  /* Seven columns fit a 1180px container at this minimum. Any wider and the
     last one wraps to a row of its own, which reads like an afterthought. */
  display: grid; grid-template-columns: repeat(auto-fit, minmax(136px, 1fr));
  gap: 24px 16px;
}
.foot-col h3 {
  font-size: 12px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--text); margin: 0 0 10px;
}
.foot-col ul { list-style: none; margin: 0; padding: 0; }
.foot-col li { margin-bottom: 6px; }
.foot-col a { font-size: 13px; color: var(--muted); text-decoration: none; }
.foot-col a:hover { color: var(--accent); }

.foot-base {
  display: flex; justify-content: space-between; flex-wrap: wrap; gap: 8px;
  margin-top: 28px; padding-top: 18px; border-top: 1px solid var(--border);
  font-size: 12px; color: var(--muted);
}
.foot-base p { margin: 0; }
.foot-base a { color: var(--muted); text-decoration: none; }
.foot-base a:hover { color: var(--accent); }

/* ---------- Comparison and reference pages ---------- */

.compare-point { padding: 14px 0; border-bottom: 1px solid var(--border); }
.compare-point:last-child { border-bottom: none; }
.compare-point h3 { font-size: 14px; margin: 0 0 5px; }
.compare-point p { margin: 0; color: var(--muted); font-size: 14px; }

table.ref { width: 100%; border-collapse: collapse; font-size: 13px; }
table.ref th, table.ref td {
  text-align: left; padding: 9px 10px; border-bottom: 1px solid var(--border);
}
table.ref th {
  font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted); font-weight: 650;
}
table.ref td a { color: var(--accent); text-decoration: none; font-weight: 600; }
table.ref tr:last-child td { border-bottom: none; }

dl.def { margin: 0; }
dl.def dt { font-weight: 650; font-size: 14px; margin-top: 12px; }
dl.def dt:first-child { margin-top: 0; }
dl.def dd { margin: 3px 0 0; color: var(--muted); font-size: 14px; }

code {
  font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--surface-2); padding: 1px 5px; border-radius: 4px;
}

/* ---------- Converter search ---------- */

.search {
  position: relative;
  flex: 0 1 260px;
  min-width: 140px;
  margin-left: auto;
}
.search input {
  width: 100%;
  padding: 7px 10px 7px 30px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  font-size: 14px;
}
.search input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--surface);
}
/* A real magnifier, not two pseudo-elements pretending to be one.
   The circle-and-rotated-bar trick reads as a magnifier at exactly one size
   and has to be re-tuned for every other, which is why the hero version
   needed its own four lines of offsets underneath. */
.search-icon {
  position: absolute; left: 12px; top: 50%;
  transform: translateY(-50%);
  color: var(--muted);
  pointer-events: none;
}

.search-results {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 70;
  width: min(380px, calc(100vw - 32px));
  max-height: 420px; overflow-y: auto;
  padding: 5px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.search-hit {
  display: flex; flex-direction: column; gap: 1px;
  padding: 8px 10px; border-radius: 7px;
  text-decoration: none; color: var(--text);
}
.search-hit:hover, .search-hit.is-active { background: var(--accent-soft); }
.search-hit b { font-size: 14px; font-weight: 650; }
.search-hit span { font-size: 12px; color: var(--muted); }

.search-empty { padding: 14px; font-size: 13px; color: var(--muted); }
.search-empty a { color: var(--accent); }

/* The hub gets a larger version of the same control. */
.search-hero { max-width: 520px; margin: 0 auto 28px; flex: none; }
.search-hero input { padding: 11px 14px 11px 42px; font-size: 15px; }
.search-hero .search-icon { left: 15px; }
.search-hero .search-results { left: 0; right: auto; width: 100%; }

@media (max-width: 720px) {
  .search { flex-basis: 150px; }
}

/* ---------- Before and after slider ---------- */

.compare {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background:
    linear-gradient(45deg, var(--surface-2) 25%, transparent 25%) 0 0/16px 16px,
    linear-gradient(-45deg, var(--surface-2) 25%, transparent 25%) 0 8px/16px 16px,
    linear-gradient(45deg, transparent 75%, var(--surface-2) 75%) 8px -8px/16px 16px,
    linear-gradient(-45deg, transparent 75%, var(--surface-2) 75%) -8px 0/16px 16px;
  cursor: ew-resize;
  touch-action: none;
  user-select: none;
}

.compare-pane { position: absolute; inset: 0; }
.compare-pane img {
  width: 100%; height: 100%;
  object-fit: contain;
  display: block;
  pointer-events: none;
}

.compare-tag {
  position: absolute; bottom: 10px;
  padding: 3px 9px; border-radius: 999px;
  background: rgba(15, 23, 42, .78); color: #fff;
  font-size: 11px; font-weight: 650; letter-spacing: .02em;
  pointer-events: none;
}
.compare-tag-left { left: 10px; }
.compare-tag-right { right: 10px; }

.compare-handle {
  position: absolute; top: 0; bottom: 0; left: 50%;
  width: 2px; margin-left: -1px;
  background: #fff;
  box-shadow: 0 0 0 1px rgba(15, 23, 42, .25);
  cursor: ew-resize;
}
.compare-handle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.compare-grip {
  position: absolute; top: 50%; left: 50%;
  width: 34px; height: 34px; margin: -17px 0 0 -17px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 2px 8px rgba(15, 23, 42, .3);
  display: grid; place-items: center;
}
.compare-grip::before {
  content: '\2039\00a0\203a';
  /* The grip is always light, so its glyph is always dark. A token here
     would follow the theme and vanish into its own handle. */
  font-size: 15px; font-weight: 700; color: #16181d; line-height: 1;
}
.compare.is-dragging .compare-grip { transform: scale(1.08); }

.compare-hint {
  margin: 8px 0 0; font-size: 12px; color: var(--muted); text-align: center;
}

/* ---------- Drop zone ---------- */

.drop-card { padding: 20px; }

/* The mode line, as a line.
 *
 * This was a coloured capsule stretched across the page. It carried two words
 * of real information and a great deal of decoration, and a tinted pill above
 * the thing you actually came to use is the visual signature of a template.
 * The words are worth keeping; the capsule is not. */
.mode-badge {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  margin: 0 0 10px;
  font-size: 13px;
}
.mode-badge b { color: var(--text); font-weight: 600; }
.mode-badge span { color: var(--muted); }

.drop {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 44px 24px;
  border: 2px dashed var(--border);
  border-radius: var(--radius);
  text-align: center;
  cursor: pointer;
  background: var(--surface);
  transition: border-color .15s, background .15s;
}
.drop:hover { border-color: var(--accent); background: var(--accent-soft); }
.drop.is-over {
  border-color: var(--accent);
  background: var(--accent-soft);
  border-style: solid;
}
.drop.is-over .drop-icon { transform: translateY(-3px); }

.drop-icon {
  width: 40px; height: 40px;
  color: var(--accent);
  transition: transform .15s;
}
.drop strong { font-size: 16px; font-weight: 650; }
/* The button is a span and a direct child, so a plain `.drop > span` rule
   caught it too and painted it muted grey - on an accent-blue background,
   that is 1.08:1 contrast, which is not "low", it is invisible. It is what
   made Choose file look like a broken image. */
.drop > span:not(.drop-btn) { font-size: 13px; color: var(--muted); max-width: 46ch; }
.drop input { display: none; }

.drop-btn {
  display: inline-block;
  margin-top: 10px; padding: 10px 22px;
  border-radius: 8px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 14.5px; font-weight: 650;
  box-shadow: 0 1px 2px rgba(0,0,0,.18);
}
.drop:hover .drop-btn { filter: brightness(1.07); }

.drop-works {
  margin: 12px 0 0; text-align: center;
  font-size: 12px; color: var(--muted);
}

@media (max-width: 600px) {
  .drop { padding: 32px 16px; }
  .drop-icon { width: 32px; height: 32px; }
}

/* ---------- Photo mockup ----------
 * The artwork on a photograph of the real product. There is no generated
 * garment behind this any more, so there is no orbit cursor either: the only
 * thing that moves is the print, dragged by its corners. */

.viewer-wrap { position: relative; }

#mockCanvas {
  /* Display size only. The backing store is set in device pixels by the
     placer, which is what keeps the print as sharp as the photo under it. */
  width: 100%;
  display: block;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background:
    linear-gradient(45deg, var(--surface-2) 25%, transparent 25%) 0 0/18px 18px,
    linear-gradient(-45deg, var(--surface-2) 25%, transparent 25%) 0 9px/18px 18px,
    linear-gradient(45deg, transparent 75%, var(--surface-2) 75%) 9px -9px/18px 18px,
    linear-gradient(-45deg, transparent 75%, var(--surface-2) 75%) -9px 0/18px 18px;
  /* A move cursor, not a grab: the photo stays put and the print is what
     the pointer picks up. */
  cursor: move;
  touch-action: none;
}
#mockCanvas:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.viewer-hint {
  position: absolute; left: 50%; bottom: 10px;
  transform: translateX(-50%);
  padding: 4px 11px; border-radius: 999px;
  background: rgba(15, 23, 42, .68); color: #fff;
  font-size: 11px; white-space: nowrap;
  pointer-events: none;
}

.viewer-actions {
  display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px;
}
.viewer-actions label.btn { cursor: pointer; }

/* ------------------------------------------------------------- editor ----
 * A workspace, not a form. The canvas gets the space and the controls sit
 * around its edge, because the thing being edited is what people need to see.
 */

.wrap-wide { max-width: 1400px; }
.hero-tight { margin-bottom: 16px; }

.editor {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  margin-bottom: 28px;
}

.editor-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}

.bar-sep {
  width: 1px;
  align-self: stretch;
  margin: 2px 4px;
  background: var(--border);
}

.zoom-label {
  margin-left: auto;
  font-size: 13px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* Height is clamped rather than left to grow: an editor taller than the
 * window means scrolling the page to reach the canvas and scrolling back to
 * reach the controls, every single edit. */
.editor-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 280px;
  height: clamp(460px, 68vh, 780px);
}

/* The canvas is checkered so a transparent area reads as transparent rather
 * than as white artwork, which is the difference between a cutout that works
 * and one that gets printed with a white box around it. */
.editor-stage {
  position: relative;
  background-color: #eef1f5;
  background-image:
    linear-gradient(45deg, #dde2e9 25%, transparent 25%, transparent 75%, #dde2e9 75%),
    linear-gradient(45deg, #dde2e9 25%, transparent 25%, transparent 75%, #dde2e9 75%);
  background-size: 18px 18px;
  background-position: 0 0, 9px 9px;
}

/* The dark checker has to stay behind a dark-mode condition. Written as a
 * bare :root:not([data-theme="light"]) it matches a page with no theme
 * attribute at all, which is every page in light mode, and the canvas comes
 * out black and white. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .editor-stage {
    background-color: #1a2233;
    background-image:
      linear-gradient(45deg, #131a28 25%, transparent 25%, transparent 75%, #131a28 75%),
      linear-gradient(45deg, #131a28 25%, transparent 25%, transparent 75%, #131a28 75%);
  }
}

:root[data-theme="dark"] .editor-stage {
  background-color: #1a2233;
  background-image:
    linear-gradient(45deg, #131a28 25%, transparent 25%, transparent 75%, #131a28 75%),
    linear-gradient(45deg, #131a28 25%, transparent 25%, transparent 75%, #131a28 75%);
}

#editCanvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;   /* the canvas handles its own pan and zoom */
  cursor: crosshair;
}

/* The drop area takes the canvas's column only. Spanning both, it pushed
 * the shapes and appearance panel onto a second row inside the editor's
 * fixed height: squeezed under the drop area behind a scrollbar, nowhere
 * near the canvas it controls. */
.editor-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px;
  grid-column: 1;
  grid-row: 1;
}


.drop-editor { width: 100%; max-width: 520px; }

.editor-side {
  border-left: 1px solid var(--border);
  padding: 4px 0;
  overflow-y: auto;
}

.side-block { padding: 14px 14px 16px; border-bottom: 1px solid var(--border); }
.side-block:last-child { border-bottom: 0; }

.side-block h3 {
  margin: 0 0 10px;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.side-info { margin: 0 0 10px; font-size: 13px; color: var(--muted); }
.side-note { margin: 8px 0 10px; font-size: 12px; color: var(--muted); }
.side-unit { font-size: 13px; color: var(--muted); }

.side-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.side-row label { flex: 0 0 52px; font-size: 13px; }
.side-row input[type="color"] {
  width: 38px; height: 28px; padding: 0;
  border: 1px solid var(--border); border-radius: 6px;
  background: none; cursor: pointer;
}
.side-row input[type="number"] {
  width: 80px; padding: 5px 7px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--surface); color: var(--text); font: inherit; font-size: 13px;
}

.btn-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px;
  margin-bottom: 8px;
}

.btn-xs {
  padding: 5px 6px;
  font-size: 12px;
  border-radius: 6px;
}

/* Layer rows are dense on purpose: a traced photo can have hundreds, and
 * scrolling past them to reach the one you want is the whole cost. */
.layer-list { max-height: 260px; overflow-y: auto; margin: 0 -6px; }

.layer-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 6px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 13px;
}
.layer-row:hover { background: var(--surface-2); }
.layer-row.is-sel { background: var(--accent-soft); color: var(--accent); }

.layer-chip {
  width: 14px; height: 14px; flex: none;
  border: 1px solid var(--border);
  border-radius: 3px;
}

.layer-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.layer-btn {
  border: 0; background: none; cursor: pointer;
  padding: 2px 3px; font-size: 12px; line-height: 1;
  color: var(--muted);
}
.layer-btn:hover { color: var(--text); }

.editor-notes { padding: 0 12px; }
.editor-notes:not(:empty) { padding: 12px; border-top: 1px solid var(--border); }

.keys { width: 100%; border-collapse: collapse; font-size: 14px; }
.keys td { padding: 7px 0; border-bottom: 1px solid var(--border); vertical-align: top; }
.keys td:first-child { width: 42%; padding-right: 16px; color: var(--text); font-weight: 500; }
.keys td:last-child { color: var(--muted); }
.keys tr:last-child td { border-bottom: 0; }

@media (max-width: 900px) {
  .editor-body { grid-template-columns: minmax(0, 1fr); height: auto; }
  .editor-side { border-left: 0; border-top: 1px solid var(--border); }
  .editor-stage { height: 56vh; min-height: 360px; }
}

/* --------------------------------------------------------- photo tools ----
 * Before and after at the same size, side by side, with the byte counts
 * under each. That comparison is the entire answer these tools give.
 */

.photo-tool {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  margin-bottom: 28px;
  overflow: hidden;
}

.photo-empty { padding: 28px; }

.photo-work {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 280px;
}

.ph-compare {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
  padding: 16px;
}

.ph-pane { margin: 0; min-width: 0; }

.ph-pane figcaption {
  margin-top: 8px;
  font-size: 13px;
  font-weight: 600;
  text-align: center;
}

/* Checkered, so a transparent result reads as transparent rather than as
 * white artwork. On a background remover that is the whole result. */
.ph-frame {
  aspect-ratio: 4 / 3;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  background-color: #eef1f5;
  background-image:
    linear-gradient(45deg, #dde2e9 25%, transparent 25%, transparent 75%, #dde2e9 75%),
    linear-gradient(45deg, #dde2e9 25%, transparent 25%, transparent 75%, #dde2e9 75%);
  background-size: 16px 16px;
  background-position: 0 0, 8px 8px;
}

.ph-frame img {
  max-width: 100%;
  max-height: 100%;
  display: block;
}

.ph-info {
  display: block;
  margin-top: 2px;
  font-weight: 400;
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.ph-info.is-warn { color: #b45309; }

.ph-controls {
  border-left: 1px solid var(--border);
  padding: 16px 14px;
}

.ph-controls h3 {
  margin: 0 0 12px;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.ph-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}
.ph-row label { flex: 0 0 84px; font-size: 13px; }
.ph-row input[type="range"] { flex: 1; min-width: 90px; }
.ph-row input[type="number"], .ph-row select {
  flex: 1; min-width: 80px; padding: 6px 8px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--surface); color: var(--text); font: inherit; font-size: 13px;
}
.ph-row output {
  flex: 0 0 44px; text-align: right;
  font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums;
}
.ph-unit { font-size: 12px; color: var(--muted); }
.ph-check, .ph-row .ph-check { display: flex; align-items: center; gap: 7px; flex: 1 1 auto; font-size: 13px; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ph-frame {
    background-color: #1a2233;
    background-image:
      linear-gradient(45deg, #131a28 25%, transparent 25%, transparent 75%, #131a28 75%),
      linear-gradient(45deg, #131a28 25%, transparent 25%, transparent 75%, #131a28 75%);
  }
}
:root[data-theme="dark"] .ph-frame {
  background-color: #1a2233;
  background-image:
    linear-gradient(45deg, #131a28 25%, transparent 25%, transparent 75%, #131a28 75%),
    linear-gradient(45deg, #131a28 25%, transparent 25%, transparent 75%, #131a28 75%);
}

@media (max-width: 860px) {
  .photo-work { grid-template-columns: minmax(0, 1fr); }
  .ph-controls { border-left: 0; border-top: 1px solid var(--border); }
  .ph-compare { grid-template-columns: minmax(0, 1fr); }
}

/* ---------------------------------------------------------- QR generator ----
 * One pane, not a before/after pair, and a square frame: a QR code is square
 * and letterboxing it in the photo tools' 4:3 frame wastes a third of it. The
 * frame is plain, not checkered, so the preview shows the colours chosen. */
.qr-compare { grid-template-columns: minmax(0, 1fr); }
.qr-frame {
  aspect-ratio: 1 / 1;
  max-width: 420px;
  width: 100%;
  margin: 0 auto;
  background: var(--surface);
  background-image: none;
}
.qr-frame svg { width: 100%; height: 100%; display: block; }
.qr-empty { margin: 0; padding: 24px; text-align: center; color: var(--muted); font-size: 14px; }
.qr-tool .ph-row input[type="text"],
.qr-tool .ph-row input[type="url"],
.qr-tool .ph-row input[type="email"],
.qr-tool .ph-row input[type="tel"],
.qr-tool .ph-row textarea {
  flex: 1; min-width: 0; width: 100%; padding: 6px 8px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--surface); color: var(--text); font: inherit; font-size: 13px;
}
.qr-tool .ph-row textarea { resize: vertical; }
.qr-tool .ph-row input[type="color"] {
  flex: 0 0 44px; height: 30px; padding: 2px;
  border: 1px solid var(--border); border-radius: 6px; background: var(--surface);
}
.qr-tool .ph-controls h3:not(:first-child) { margin-top: 18px; }
.qr-tool .btn-block + .btn-block { margin-top: 8px; }

/* ---------------------------------------------------------------- icons ----
 * Inline SVG that inherits colour and sits on the text baseline, replacing
 * the emoji and dingbats the UI used to draw with. Those pick up their own
 * colour from the platform font, ignore the theme, and differ on every OS.
 */

.icon {
  display: inline-block;
  vertical-align: -0.15em;
  flex: none;
}

.btn-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 7px 9px;
}

.layer-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* The click target is the button, so the icon must not swallow the event. */
.layer-btn svg { pointer-events: none; }

.dock-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.caret {
  display: inline-flex;
  align-items: center;
  margin-left: 3px;
  opacity: 0.75;
}

/* A checkbox that sits on one line with its label, for settings that are a
 * yes or no rather than a value. */
.check {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  cursor: pointer;
}
.check input { margin: 0; }

/* --------------------------------------------------------------- viewer ----
 * Facts beside the picture, not under it. The dimensions and the unit
 * declaration are what someone opened the file to find out; the picture is
 * how they confirm it is the right file.
 */

.viewer {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  margin-bottom: 28px;
}

.viewer-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  min-height: 480px;
}

.viewer-main { position: relative; min-width: 0; }
.viewer-empty { padding: 28px; }

.viewer-stage {
  position: relative;
  height: clamp(380px, 58vh, 700px);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-2);
}

#viewerCanvas { display: block; width: 100%; height: 100%; touch-action: none; }
#viewerImage { max-width: 100%; max-height: 100%; display: block; }

.view3d-hint {
  position: absolute;
  left: 12px;
  bottom: 12px;
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  padding: 6px 8px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  border: 1px solid var(--border);
}
.view3d-hint .hint { margin: 0 0 0 4px; font-size: 12px; }

.viewer-side {
  border-left: 1px solid var(--border);
  padding: 16px 14px;
  overflow-y: auto;
}
.viewer-side h3 {
  margin: 0 0 12px;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.facts { margin: 0; }
.fact {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 0;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
}
.fact:last-child { border-bottom: 0; }
.fact dt { color: var(--muted); flex: none; }
.fact dd {
  margin: 0;
  text-align: right;
  font-variant-numeric: tabular-nums;
  word-break: break-word;
}
.fact dd .warn { color: #b45309; }

@media (max-width: 900px) {
  .viewer-body { grid-template-columns: minmax(0, 1fr); }
  .viewer-side { border-left: 0; border-top: 1px solid var(--border); }
  .viewer-stage { height: 52vh; min-height: 320px; }
}

/* ---------------------------------------------------------------- batch ----
 * A queue with a row per file. The per-file status is the point: one corrupt
 * file in a folder of forty must be identifiable without re-running the rest.
 */

.drop-multi { margin: 8px 0 0; font-size: 13px; color: var(--muted); }

.batch-summary {
  margin: 0 0 10px;
  font-size: 14px;
  font-weight: 500;
}

.batch-bar {
  height: 6px;
  border-radius: 3px;
  background: var(--surface-2);
  overflow: hidden;
  margin-bottom: 12px;
}
.batch-bar span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--accent);
  transition: width 0.2s ease;
}

.batch-list {
  max-height: 320px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 8px;
  margin-bottom: 14px;
}

.batch-row {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) auto 84px;
  align-items: center;
  gap: 10px;
  padding: 7px 10px;
  font-size: 13px;
  border-bottom: 1px solid var(--border);
}
.batch-row:last-child { border-bottom: 0; }

.batch-n { color: var(--muted); font-variant-numeric: tabular-nums; }
.batch-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.batch-detail { color: var(--muted); font-size: 12px; text-align: right; }
.batch-status {
  text-align: right;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
}
.batch-row.is-working .batch-status { color: var(--accent); }
.batch-row.is-done .batch-status { color: #15803d; }
.batch-row.is-failed .batch-status { color: #b91c1c; }
.batch-row.is-failed .batch-detail { color: #b91c1c; }

.batch-formats {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  margin: 0 0 12px;
}
.batch-formats legend {
  padding: 0 6px;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.batch-actions { display: flex; flex-wrap: wrap; gap: 8px; }

@media (max-width: 560px) {
  .batch-row { grid-template-columns: 22px minmax(0, 1fr) 70px; }
  .batch-detail { display: none; }
}

/* A format held back for the paid plan. Shown, not hidden: someone deciding
 * whether to pay needs to see what they would get. */
.export.is-locked {
  opacity: 0.72;
  border-style: dashed;
}
.export.is-locked span { color: var(--accent); font-weight: 600; }

/* Preview surfaces under the free tier. The cursor change is the honest
 * signal that the usual right-click will not work here. */
:root[data-tier="anonymous"] [data-protected="1"],
:root[data-tier="free"] [data-protected="1"] {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
:root[data-tier="anonymous"] [data-protected="1"] img,
:root[data-tier="free"] [data-protected="1"] img,
:root[data-tier="anonymous"] [data-protected="1"] svg,
:root[data-tier="free"] [data-protected="1"] svg {
  -webkit-user-drag: none;
  pointer-events: none;
}

/* --------------------------------------------------------------- portal ----
 * The admin screen puts the audit log beside the buttons on purpose. Power
 * you can watch being used is the only kind that is safe to hand out.
 */

.portal-head { margin: 8px 0 18px; }
.portal-head h2 { margin: 0 0 2px; }

.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px;
  margin: 0 0 20px;
}

.stat {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 12px 14px;
}
.stat dt {
  font-size: 12px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 4px;
}
.stat dd {
  margin: 0;
  font-size: 22px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}
.stat small { display: block; margin-top: 3px; font-size: 12px; color: var(--muted); }

.admin-grid {
  display: grid;
  grid-template-columns: 320px minmax(0, 1fr);
  gap: 16px;
  margin-bottom: 24px;
}

.admin-side, .admin-detail {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 14px;
}

.admin-side .side-row input[type="search"] {
  width: 100%;
  padding: 7px 9px;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 14px;
}

.acct-list { max-height: 460px; overflow-y: auto; margin: 8px -6px 10px; }

.acct-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  padding: 8px 8px;
  border: 0;
  border-radius: 8px;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.acct-row:hover { background: var(--surface-2); }
.acct-row.is-sel { background: var(--accent-soft); }
.acct-main { display: flex; flex-direction: column; min-width: 0; }
.acct-main b { font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct-main small { font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct-tags { display: flex; gap: 4px; flex: none; }

.tag {
  font-size: 11px;
  padding: 2px 6px;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--muted);
  border: 1px solid var(--border);
}
.tag-admin { background: var(--accent-soft); color: var(--accent); border-color: transparent; }
.tag-warn { background: #fef3c7; color: #92400e; border-color: transparent; }
.tag-flag { background: #fee2e2; color: #991b1b; border-color: transparent; }

.detail-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 14px;
}
.detail-head h3 { margin: 0 0 2px; }
.detail-balance { text-align: right; flex: none; }
.detail-balance b { display: block; font-size: 26px; font-variant-numeric: tabular-nums; }
.detail-balance small { color: var(--muted); font-size: 12px; }
.detail-sub { margin: 18px 0 8px; font-size: 14px; }

.admin-acts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 12px;
}
.admin-acts fieldset {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  margin: 0;
  /* A grid item defaults to min-width:auto, so a row of buttons wider than
   * its column pushes out of the box and overlaps the next one instead of
   * wrapping. */
  min-width: 0;
}

/* The rows inside an action box wrap; they are buttons, not a sentence. */
.admin-acts .side-row {
  flex-wrap: wrap;
  margin-bottom: 6px;
}
.admin-acts .side-row:last-child { margin-bottom: 0; }
.admin-acts .side-row label { flex: 0 0 auto; }
.admin-acts input[type="number"] { flex: 0 0 84px; }
.admin-acts select { flex: 1 1 110px; }
.admin-acts legend {
  padding: 0 6px;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.admin-acts input[type="text"], .admin-acts input[type="number"], .admin-acts select {
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  min-width: 0;
}
.admin-acts input[type="text"] { width: 100%; }

.btn-danger {
  border-color: #fca5a5;
  color: #b91c1c;
}
.btn-danger:hover { background: #fee2e2; }

.flags { margin-bottom: 14px; }

.ledger { border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.ledger-row {
  display: grid;
  grid-template-columns: 90px 72px minmax(0, 1fr) auto;
  gap: 10px;
  align-items: center;
  padding: 7px 10px;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
}
.ledger-row:last-child { border-bottom: 0; }
.ledger-kind {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.ledger-kind.is-purchase, .ledger-kind.is-grant, .ledger-kind.is-monthly { color: #15803d; }
.ledger-kind.is-refund, .ledger-kind.is-revoke { color: #b91c1c; }
.ledger-amt { font-variant-numeric: tabular-nums; font-weight: 600; }
.ledger-note { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ledger-when { color: var(--muted); font-size: 12px; white-space: nowrap; }

.audit { border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.audit-row {
  display: grid;
  grid-template-columns: 130px minmax(0, 1fr) minmax(0, 1fr);
  gap: 10px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
}
.audit-row:last-child { border-bottom: 0; }
.audit-when { color: var(--muted); font-size: 12px; white-space: nowrap; }
.audit-who { color: var(--muted); font-size: 12px; overflow: hidden; text-overflow: ellipsis; }
.audit-note { grid-column: 2 / -1; color: var(--muted); font-style: italic; font-size: 12px; }

@media (max-width: 900px) {
  .admin-grid { grid-template-columns: minmax(0, 1fr); }
  .acct-list { max-height: 300px; }
  .audit-row { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .ledger-row { grid-template-columns: 80px 64px minmax(0, 1fr); }
  .ledger-when { display: none; }
}

/* ---------------------------------------------------------------- mobile ----
 * Measured before it was written: every page overflowed its viewport by over
 * 200px at 360 and 390 wide, because the header laid out as one horizontal
 * row of nav items, a search box and a theme toggle that simply ran off the
 * screen. One cause, every page.
 *
 * The header collapses to a menu button below 860px. The nav becomes a
 * drawer under the bar rather than a full-screen overlay, so the page behind
 * it stays visible and a mis-tap does not lose someone's place.
 */

.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  margin-left: auto;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
}
.nav-toggle:hover { background: var(--surface-2); }

@media (max-width: 860px) {
  /* The preview button keeps its place in the bar at every width, because it
   * is a tool action rather than navigation, and because hiding it behind the
   * menu is what made the preview unreachable on a phone. */
  .nav-anchor { order: 1; margin-left: auto; }
  #navPreview { min-height: 42px; padding: 8px 12px; }

  .topbar {
    flex-wrap: wrap;
    height: auto;
    min-height: var(--topbar-h);
    padding: 8px 16px;
    row-gap: 8px;
  }

  /* Logo on the left; Preview, the theme toggle and the menu button
   * together on the right, the menu button last, where a thumb expects it. */
  .nav-toggle { display: inline-flex; margin-left: 6px; order: 3; }
  #themeToggle { order: 2; margin-left: 6px; width: 42px; height: 42px; }

  /* The drawer takes the full width of the bar, on its own row. */
  .nav {
    order: 4;
    flex: 0 0 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    padding-bottom: 6px;
  }

  .nav-item {
    width: 100%;
    justify-content: space-between;
    /* 44px is the smallest target that is reliably hit with a thumb. The
     * measured height before this was 38. */
    min-height: 44px;
    padding: 10px 12px;
    font-size: 15px;
  }

  /* Full width inside the drawer only. This also matched the Preview button,
     which lives in the bar itself, and pushed it onto a row of its own. */
  .nav .nav-anchor { width: 100%; }
  /* The anchor is a row on wide screens; in the drawer the menu opens under
     its button, not beside it, which squeezed both into half the width. */
  .nav .nav-anchor.has-menu { flex-direction: column; align-items: stretch; }

  /* A dropdown inside a drawer is a trap on a phone: it opens under the
   * finger and off the bottom of the screen. These become plain sections. */
  .nav-menu {
    position: static;
    width: 100%;
    box-shadow: none;
    border-left: 2px solid var(--border);
    border-radius: 0;
    margin: 2px 0 6px 10px;
  }
  .nav-menu a { min-height: 44px; display: flex; align-items: center; }

  /* The search that used to sit here is gone. It is on the converters hub
     now, which is where somebody looking for a converter already is; in the
     bar it cost a whole row of a sticky header on a phone. */
  .search input { min-height: 44px; }
  .search-results { max-height: 60vh; }

  .brand { margin-right: 0; min-height: 44px; align-items: center; }
  .brand .logo { height: 21px; }

  #themeToggle {
    order: 2;
    flex: 0 0 auto;
    min-width: 42px;
    min-height: 42px;
  }

  /* The preview dock becomes a bottom sheet. Anchored under the top bar it
   * would cover the whole screen and the artwork behind it. */
  .preview-dock {
    position: fixed;
    left: 0;
    right: 0;
    top: auto;
    bottom: 0;
    width: 100%;
    max-height: 78vh;
    border-radius: 14px 14px 0 0;
    box-shadow: 0 -8px 30px rgba(15, 23, 42, 0.18);
  }
  .dock-stage { max-height: 44vh; }
  .dock-tabs { overflow-x: auto; -webkit-overflow-scrolling: touch; }

  /* Tables of specifications are the one thing that cannot reflow. Let them
   * scroll sideways inside their own box rather than widening the page. */
  .keys, .cmp-table, table {
    display: block;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
}

/* Anything the layout cannot help: never let the page itself scroll sideways.
 * A horizontal scroll on a phone makes every vertical swipe feel broken. */
html, body { overflow-x: hidden; }

/* Touch targets, everywhere, not only in the header.
 *
 * Measured, not guessed: the sliders came out 16px tall and the checkboxes
 * 13px. A 16px slider is not draggable with a thumb, it is a coin toss
 * between moving it and scrolling the page, and these sliders are how the
 * trace is controlled.
 */
@media (pointer: coarse) {
  .btn, .btn-sm, button, .nav-item, .layer-btn, .export {
    min-height: 44px;
  }
  .btn-xs { min-height: 38px; }
  .layer-btn { min-width: 38px; }
  .acct-row { min-height: 48px; }

  input[type="text"], input[type="search"], input[type="number"],
  input[type="email"], select, textarea {
    min-height: 44px;
    /* Anything under 16px makes iOS zoom the page on focus, and it does not
     * zoom back out. */
    font-size: 16px;
  }

  /* The track stays slim; the control gets a tall transparent hit area
   * around it, so it looks the same and can actually be grabbed. */
  input[type="range"] {
    height: 44px;
    padding: 14px 0;
    background: transparent;
    -webkit-appearance: none;
    appearance: none;
    touch-action: pan-y;
  }
  input[type="range"]::-webkit-slider-runnable-track {
    height: 6px;
    border-radius: 3px;
    background: var(--surface-2);
    border: 1px solid var(--border);
  }
  input[type="range"]::-moz-range-track {
    height: 6px;
    border-radius: 3px;
    background: var(--surface-2);
    border: 1px solid var(--border);
  }
  input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 26px;
    height: 26px;
    margin-top: -11px;
    border-radius: 50%;
    background: var(--accent);
    border: 2px solid var(--surface);
    box-shadow: 0 1px 3px rgba(15, 23, 42, 0.3);
  }
  input[type="range"]::-moz-range-thumb {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--accent);
    border: 2px solid var(--surface);
  }

  input[type="checkbox"], input[type="radio"] {
    width: 22px;
    height: 22px;
  }

  input[type="color"] {
    width: 48px;
    height: 44px;
  }

  /* A label wrapping a checkbox is the real target, so the whole row is made
   * tappable rather than the 22px box alone. */
  .check, .ph-check, .field label { min-height: 44px; display: flex; align-items: center; gap: 8px; }
  .field label:has(input[type="range"]) { display: block; min-height: 0; }

  /* These two are scoped by a class elsewhere in the file, which outranks the
   * bare element selectors above. Restated at matching specificity rather
   * than reached for with !important. */
  .side-row input[type="color"],
  .ph-row input[type="color"] { width: 48px; height: 44px; }
  .side-row input[type="number"],
  .ph-row input[type="number"],
  .admin-acts input[type="number"],
  .admin-acts input[type="text"],
  .admin-acts select,
  .admin-side .side-row input[type="search"] { min-height: 44px; font-size: 16px; }

  /* Secondary buttons are smaller by design, but not below the point where a
   * thumb misses them. */
  .btn-xs { min-height: 40px; padding-left: 10px; padding-right: 10px; }
}

/* Prose pages: about, contact, the legal ones. A measure of around 70
 * characters is where reading speed peaks; the site's default content width
 * is set for tool pages with a sidebar, which is too wide for running text. */
.wrap-prose { max-width: 760px; }

.prose h2 {
  margin: 28px 0 8px;
  font-size: 19px;
}
.prose h2:first-child { margin-top: 0; }
.prose p {
  margin: 0 0 14px;
  line-height: 1.65;
  color: var(--text);
}

/* ---------- Dedicated preview and mockup route ----------
 * A split view: the stage takes the room, the rail holds everything that
 * drives it. Below 960px the rail moves under the stage rather than getting
 * narrower, because a control rail thin enough to sit beside a viewer on a
 * phone is too thin to use. */

.mockup-page { max-width: 1500px; margin: 0 auto; padding: 18px 16px 48px; }
.mockup-head { margin-bottom: 16px; }
.mockup-head h1 { margin: 6px 0 4px; }
.mockup-head .lede { max-width: 62ch; color: var(--muted); }

/* Controls above, viewer beneath.
   This was two columns with the viewer on the left and a tall stack of cards
   on the right. The rail is six cards deep and the viewer is at most 720px,
   so by the time you had scrolled to Placement or Download the product had
   left the screen entirely: you were moving sliders with nothing to look at.
   The controls now sit across the top in as many columns as fit, and the
   viewer runs full width underneath them, where a change is visible the
   moment you make it. */
.mockup-split { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; }

.mockup-stage {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); overflow: hidden;
}
.stage-tabs { display: flex; gap: 2px; padding: 8px 8px 0; border-bottom: 1px solid var(--border); }
.stage-tabs button {
  padding: 9px 14px; border: 1px solid transparent; border-bottom: none;
  border-radius: 8px 8px 0 0; background: none; color: var(--muted);
  cursor: pointer; font: inherit; font-size: 14px;
}
.stage-tabs button:hover { color: var(--text); }
.stage-tabs button.is-active {
  background: var(--surface-2); border-color: var(--border);
  color: var(--text); font-weight: 600;
}
.stage-body { position: relative; }
.stage-pane { position: relative; }

/* The viewer gets real height. A 3D product in a 220px box is a thumbnail,
   and the whole point of this route is that it is not one. */
#glCanvas, #photoCanvas {
  display: block; width: 100%; height: clamp(380px, 68vh, 780px);
  background:
    radial-gradient(120% 90% at 50% 8%, var(--surface) 0%, var(--surface-2) 70%);
  touch-action: none;
}
#glCanvas { cursor: grab; }
#glCanvas:active { cursor: grabbing; }
#photoCanvas { cursor: move; height: auto; }

.stage-hint {
  position: absolute; left: 50%; bottom: 12px; transform: translateX(-50%);
  padding: 5px 12px; border-radius: 999px;
  background: rgba(15, 23, 42, .68); color: #fff;
  font-size: 11.5px; white-space: nowrap; pointer-events: none;
}
.stage-note {
  position: absolute; left: 16px; right: 16px; top: 16px;
  padding: 10px 13px; border-radius: 8px;
  background: var(--warn-soft, #fef3c7); color: var(--warn-ink, #713f12);
  border: 1px solid var(--warn-border, #fcd34d);
  font-size: 13.5px; line-height: 1.45;
}
.stage-art { padding: 18px; }
.art-frame {
  min-height: clamp(320px, 55vh, 660px);
  display: flex; align-items: center; justify-content: center;
  border-radius: 8px;
  background:
    linear-gradient(45deg, var(--surface-2) 25%, transparent 25%) 0 0/20px 20px,
    linear-gradient(-45deg, var(--surface-2) 25%, transparent 25%) 0 10px/20px 20px,
    linear-gradient(45deg, transparent 75%, var(--surface-2) 75%) 10px -10px/20px 20px,
    linear-gradient(-45deg, transparent 75%, var(--surface-2) 75%) -10px 0/20px 20px;
}
.art-shown { max-width: 100%; max-height: 62vh; display: block; }

/* One compact bar, not six stacked cards.
   Six cards laid out across the top came to 660px before the viewer even
   started, which pushed the product off a 900px screen: the same fault as
   the two-column version, with the parts in a different order. Laid out as
   rows of controls the whole bar is about 150px, so the viewer begins in
   the first screenful and a slider change is visible without scrolling. */
.mockup-bar {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); padding: 11px 14px;
  display: grid; gap: 9px;
}
/* min-width: 0 all the way down, or the sideways product scroller sets the
   whole column's minimum to its own content: at 390px the bar measured 797px
   wide and everything past the fold was simply clipped. A flex or grid item
   will not shrink below min-content unless it is told it may. */
.mockup-bar { min-width: 0; }
.bar-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; min-width: 0; }
.bar-row + .bar-row { border-top: 1px solid var(--border); padding-top: 9px; }
.bar-group { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 8px; min-width: 0; }
.bar-actions { margin-left: auto; }
.bar-label {
  font-size: 11px; text-transform: uppercase; letter-spacing: .07em;
  font-weight: 700; color: var(--muted);
}
.bar-label-in { margin: 0 2px 0 6px; }
.mockup-bar .hint { margin: 0; font-size: 12px; }
.mockup-bar .btn { margin: 0; }

.prod-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-width: 0; }
.seg-sm button { padding: 5px 10px; font-size: 12.5px; }

.bar-place .field { display: flex; align-items: center; gap: 9px; margin: 0;
                    flex: 1 1 210px; min-width: 190px; }
.bar-place .field label { display: flex; gap: 6px; align-items: baseline; margin: 0;
                          font-size: 12.5px; white-space: nowrap; }
.bar-place .field input[type="range"] { flex: 1; min-width: 80px; margin: 0; }

.stage-foot { display: grid; gap: 6px; }
.stage-foot .hint { margin: 0; }

/* Settings a route does not lead with, folded rather than dropped.
   Every page still produces every format, so the controls have to stay
   reachable; what they must not do is stand between somebody converting a
   PNG to a GIF and the Convert button. */
.more-settings { margin-top: 12px; }
.more-settings > summary {
  cursor: pointer; list-style: none;
  padding: 11px 14px; border: 1px dashed var(--border); border-radius: var(--radius);
  color: var(--muted); font-size: 13.5px; font-weight: 600;
  background: var(--surface);
}
.more-settings > summary::-webkit-details-marker { display: none; }
.more-settings > summary::before { content: '+ '; font-weight: 700; }
.more-settings[open] > summary::before { content: '\2212 '; }
.more-settings > summary:hover { color: var(--text); border-color: var(--muted); }
.more-settings > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.more-settings[open] > summary { margin-bottom: 12px; }

/* On a phone the bar has to earn its place above the viewer.
   Wrapped at 390px it came to 486px, which put the product below the fold
   again. The product list scrolls sideways on one line instead of stacking
   into four, the heading gives back the space it does not need, and the
   viewer is shorter, so the controls and the thing they control are on the
   same screen. */
@media (max-width: 700px) {
  .mockup-head h1 { font-size: 23px; line-height: 1.2; margin: 4px 0 3px; }
  .mockup-head .lede { font-size: 13.5px; }
  .mockup-bar { padding: 9px 10px; gap: 7px; }
  .bar-row + .bar-row { padding-top: 7px; }
  .bar-actions { margin-left: 0; }
  .prod-chips {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    flex: 1 1 100%; padding-bottom: 2px;
  }
  .prod-chips::-webkit-scrollbar { display: none; }
  .prod-chips .prod-pick { flex: 0 0 auto; }
  .bar-place .field { flex: 1 1 100%; }
  #glCanvas, #photoCanvas { height: clamp(280px, 42vh, 400px); }
}
.rail-sub { font-size: 12px; text-transform: uppercase; letter-spacing: .06em;
            color: var(--muted); margin: 14px 0 6px; }

.prod-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px; }
.prod-chips .prod-pick { padding: 6px 11px; font-size: 13px; border-radius: 999px; }
.prod-pick {
  padding: 9px 8px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--text);
  cursor: pointer; font: inherit; font-size: 13px; text-align: left;
}
.prod-pick:hover { border-color: var(--accent); }
.prod-pick.is-active { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
/* A garment with no model yet is still offered, because choosing it is how
   you find out and the note explains what to do instead. */
.prod-pick[data-generated="0"]::after { content: ' \00b7 model'; opacity: .55; font-size: 11px; }

.swatches { display: flex; flex-wrap: wrap; gap: 7px; }
.swatch {
  width: 28px; height: 28px; border-radius: 50%;
  border: 2px solid var(--border); cursor: pointer; padding: 0;
}
.swatch.is-active { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }


/* ---------- Back to top ----------
 * These pages are long by design: the converter, then the explanation, then
 * the related routes. Getting back to the dropzone meant a flick-scroll up
 * the whole page. It appears once you are far enough down for it to be the
 * shorter route, and it is a real button so it reaches the keyboard too. */
.to-top {
  position: fixed; right: 16px; bottom: 16px; z-index: 40;
  width: 44px; height: 44px; padding: 0;
  display: grid; place-items: center;
  border: 1px solid var(--border); border-radius: 50%;
  background: var(--surface); color: var(--text);
  box-shadow: var(--shadow); cursor: pointer;
  opacity: 0; transform: translateY(8px); pointer-events: none;
  transition: opacity .18s, transform .18s;
}
.to-top.is-on { opacity: 1; transform: none; pointer-events: auto; }
.to-top:hover { border-color: var(--accent); color: var(--accent); }
.to-top:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (max-width: 600px) { .to-top { right: 12px; bottom: 12px; } }
@media (prefers-reduced-motion: reduce) { .to-top { transition: none; } }

/* ---------- Fidelity readout ----------
 * The number beside the wipe slider. Colour-coded by how much of the original
 * survived, because the whole point is that you should not have to read it to
 * know whether something is wrong. */
.fidelity {
  margin: 10px 0 0; padding: 11px 13px;
  border: 1px solid var(--border); border-left-width: 3px;
  border-radius: 8px; background: var(--surface-2);
  font-size: 13px; line-height: 1.5;
}
.fidelity b { font-size: 15px; }
.fidelity span { display: block; color: var(--muted); font-size: 12.5px; margin-top: 2px; }
.fidelity em { display: block; font-style: normal; margin-top: 6px; }
.fidelity[data-level="ok"]   { border-left-color: var(--ok); }
.fidelity[data-level="note"] { border-left-color: var(--warn); }
.fidelity[data-level="warn"] { border-left-color: var(--danger); }

/* ------------------------------------------------------------- result bar */
/*
 * A conversion finishes below the fold. On a 390px phone the download card
 * starts 1,360px down a page that does not scroll itself, so the screen still
 * said "Choose file" after a file had been converted. This bar is the answer
 * to "where is my file": it names what came out and hands it over in one tap.
 */
.resultbar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 60;
  padding: 10px 16px calc(10px + env(safe-area-inset-bottom, 0px));
  background: var(--surface);
  border-top: 1px solid var(--border);
  box-shadow: 0 -6px 24px rgb(15 23 42 / 10%);
  transition: transform .22s ease, opacity .22s ease;
}
.resultbar-in {
  max-width: 1180px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
}
.resultbar-tick {
  display: inline-flex;
  width: 24px; height: 24px;
  flex: 0 0 24px;
  align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--ok-bg, #dcfce7);
  color: var(--ok, #15803d);
}
.resultbar-msg {
  flex: 1;
  min-width: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.resultbar-acts { display: flex; gap: 8px; flex: 0 0 auto; }

/* Once the full format grid is on screen the bar is redundant, so it drops
   out of the way rather than covering the last row of it. */
.resultbar.is-tucked {
  transform: translateY(110%);
  opacity: 0;
  pointer-events: none;
}

/* The bar floats over the page, so the page needs room to scroll past it. */
body:has(.resultbar:not([hidden])) { padding-bottom: 72px; }

@media (max-width: 560px) {
  .resultbar-in { flex-wrap: wrap; }
  .resultbar-msg { flex: 1 1 100%; white-space: normal; }
  .resultbar-acts { flex: 1 1 100%; }
  .resultbar-acts .btn { flex: 1; }
}

/* A result with nothing to compare against: DXF, PLT, G-code, EPS in, where
   the source is text and there is no bitmap to put on the other side. */
.result-only {
  display: block;
  width: 100%;
  height: auto;
  max-height: 420px;
  object-fit: contain;
  border-radius: 8px;
  /* Paper, in either theme: a cut file is black lines and a stamp is dark
     ink, and on the dark theme's grey they all but vanished - the result
     looked as if nothing had come out. Checked lightly, so a transparent
     area still reads as transparent. */
  background-color: #ffffff;
  background-image:
    linear-gradient(45deg, #eef1f5 25%, transparent 25%, transparent 75%, #eef1f5 75%),
    linear-gradient(45deg, #eef1f5 25%, transparent 25%, transparent 75%, #eef1f5 75%);
  background-size: 18px 18px;
  background-position: 0 0, 9px 9px;
}

/* ------------------------------------------------- portal header actions */
.portal-top {
  display: flex; align-items: flex-start; gap: 16px;
  flex-wrap: wrap; margin-bottom: 4px;
}
.portal-top .portal-head { flex: 1; min-width: 0; }
.portal-actions { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }

/* The balance, as a thing you can act on rather than a number in a corner. */
.credit-pill {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 12px 6px 6px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--surface);
  color: var(--text); text-decoration: none; font-size: 13px;
}
.credit-pill:hover { border-color: var(--accent); }
.credit-dot {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--accent); color: #fff; font-size: 11px; font-weight: 700;
}
.credit-pill b { font-variant-numeric: tabular-nums; }
.credit-sep { width: 1px; height: 16px; background: var(--border); }
.credit-top { color: var(--accent); font-weight: 600; }
.btn-upgrade { display: inline-flex; align-items: center; gap: 6px; }

/* ---------------------------------------------------------- workspaces */
.wsblock { margin-top: 22px; }
.wshead { margin-bottom: 10px; }
.wskicker {
  font-size: 11px; text-transform: uppercase; letter-spacing: .07em;
  color: var(--muted); font-weight: 700;
}
.wshead .hint { margin: 2px 0 0; }

/* A strip, and on a narrow screen a strip you swipe rather than a stack
   that pushes the work off the bottom of the phone. */
.wsbar {
  display: flex; gap: 4px; overflow-x: auto; padding-bottom: 6px;
  border-bottom: 1px solid var(--border);
  scrollbar-width: thin;
}
.wstab {
  display: inline-flex; align-items: center; gap: 7px; flex: 0 0 auto;
  /* 10px, not 13: at 13 the six tabs came to exactly the width of the strip
     that holds them, so activating the last one nudged the scroll by a hair
     and sliced the first label mid-word. */
  padding: 9px 10px; border: 0; border-radius: 8px;
  background: none; color: var(--muted); cursor: pointer;
  font: inherit; font-size: 13px; font-weight: 600; white-space: nowrap;
  min-height: 40px;
}
.wstab:hover { background: var(--surface-2); color: var(--text); }
.wstab.is-active { background: var(--surface); color: var(--text); box-shadow: inset 0 0 0 1px var(--border); }
.wstab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.wstab-ico { display: inline-flex; opacity: .8; }
.wstab-lock { display: inline-flex; opacity: .55; }
.wstab.is-locked { color: var(--muted); }

.wsbody { padding-top: 16px; }

/* The locked panel: what it is, and what opens it. Never an empty tab. */
.wslock {
  display: grid; justify-items: center; text-align: center; gap: 8px;
  padding: 34px 18px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface-2);
}
.wslock-tile {
  display: inline-flex; align-items: center; justify-content: center;
  width: 54px; height: 54px; border-radius: 14px;
  background: var(--accent); color: #fff; margin-bottom: 4px;
}
.wslock-tile svg { width: 24px; height: 24px; }
.wslock h3 { margin: 0; font-size: 17px; }
.wslock p { margin: 0; color: var(--muted); font-size: 14px; max-width: 46ch; }
.wslock-cta { margin-top: 8px; }
.wslock-note { font-size: 12.5px; max-width: 52ch; }

@media (max-width: 560px) {
  .portal-actions { width: 100%; }
  .credit-pill { flex: 1; justify-content: center; }
  .wslock { padding: 26px 14px; }
}

/* ------------------------------------------------- the staged drop card */
/*
 * Upload, convert, download: one card, one stage at a time. The download
 * ends up standing exactly where the dropzone was, because that is where
 * somebody is already looking when the conversion finishes.
 */
.drop-card .stage { display: block; }

.filerow {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 14px; margin-bottom: 12px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface-2);
}
.filerow-ico { display: inline-flex; color: var(--muted); flex: 0 0 auto; }
.filerow-name {
  flex: 1; min-width: 0; font-weight: 600; font-size: 14px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.filerow-size { color: var(--muted); font-size: 13px; flex: 0 0 auto; }
.filerow-x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; flex: 0 0 28px;
  border: 0; border-radius: 6px; background: none;
  color: var(--muted); cursor: pointer;
}
.filerow-x:hover { background: var(--border); color: var(--text); }

.btn-lg { padding: 14px 20px; font-size: 15px; font-weight: 650; }

.working {
  display: grid; justify-items: center; gap: 10px;
  padding: 48px 20px; text-align: center;
}
.working strong { font-size: 16px; }
.spinner-lg { width: 26px; height: 26px; border-width: 3px; }

.donehead {
  display: grid; justify-items: center; gap: 6px;
  padding: 8px 0 18px; text-align: center;
}
.donetick {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--ok-bg, #dcfce7); color: var(--ok, #15803d);
}
.donehead strong { font-size: 17px; }
.doneways {
  display: flex; gap: 8px; justify-content: center; margin-top: 10px;
  flex-wrap: wrap;
}

/* With no settings worth asking, the result takes the whole width. */
.cols:not(:has(.col-side)) { grid-template-columns: minmax(0, 1fr); }

/* Pattern generator: the preview is the tile, repeated by the browser. */
.pat-frame {
  width: 100%;
  min-height: 220px;
  background-repeat: repeat;
  background-position: 0 0;
  border-radius: var(--radius, 10px);
  border: 1px solid var(--line, #e5e7eb);
}
/* SVG animator: the preview is the downloaded file, in an <img>. */
.anim-frame { display: grid; place-items: center; min-height: 220px; background: #fff; border-radius: var(--radius, 10px); }
/* The whole artwork, always: the frame is a fixed 4:3 box, and an image only
   capped at 420px tall overflowed it and lost its bottom on a portrait or
   square logo. Contained, it is scaled to fit whatever its shape. */
.anim-frame { position: relative; }
.anim-frame img { position: absolute; inset: 8px; width: calc(100% - 16px); height: calc(100% - 16px); max-height: none; object-fit: contain; display: block; }
/* SVG animator colours: one swatch per colour in the artwork. */
.anim-colors { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 10px; }
.anim-colors-empty { font-size: .85rem; color: var(--muted, #6b7280); }
.anim-swatch { display: inline-flex; align-items: center; gap: 6px; padding: 3px 8px 3px 3px; border: 1px solid var(--line, #e5e7eb); border-radius: 999px; font: 12px/1 ui-monospace, monospace; cursor: pointer; }
.anim-swatch input[type=color] { width: 26px; height: 26px; padding: 0; border: 0; border-radius: 50%; background: none; cursor: pointer; }
.anim-swatch input[type=color]::-webkit-color-swatch-wrapper { padding: 0; }
.anim-swatch input[type=color]::-webkit-color-swatch { border: 1px solid rgba(0,0,0,.2); border-radius: 50%; }
.anim-swatch input[type=color]::-moz-color-swatch { border: 1px solid rgba(0,0,0,.2); border-radius: 50%; }
.anim-swatch.is-changed { border-color: var(--accent, #6d3fc0); }
.anim-onecolor { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.anim-onecolor input[type=color] { width: 40px; height: 28px; padding: 0; border: 1px solid var(--line, #e5e7eb); border-radius: 6px; background: none; }
/* KDP coloring pages: the book's pages as thumbnails. */
.col-pages { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 12px; }
.col-pages img { width: 100%; display: block; background: #fff; border: 1px solid var(--line, #e5e7eb); border-radius: 4px; box-shadow: 0 1px 3px rgba(0,0,0,.08); }
/* DTF gang sheet: the designs as rows, the sheet as a scaled preview. */
.gang-list { display: grid; gap: 8px; margin-bottom: 12px; }
.gang-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 8px; border: 1px solid var(--line, #e5e7eb); border-radius: 8px; background: var(--surface, #fff); }
.gang-row canvas { width: 48px; height: auto; background: repeating-conic-gradient(#e2e8f0 0 25%, #f8fafc 0 50%) 0 0 / 10px 10px; }
.gang-name { flex: 1 1 140px; font-size: 14px; overflow-wrap: anywhere; }
.gang-row input[type="number"] { width: 72px; }
.gang-dpi { font-size: 13px; font-weight: 600; }
.gang-dpi[data-level="ok"] { color: #15803d; }
.gang-dpi[data-level="low"] { color: #b45309; }
.gang-dpi[data-level="bad"] { color: #b91c1c; }
.gang-preview { display: block; max-width: 100%; height: auto; border: 1px solid var(--line, #e5e7eb); }
/* HTML to PDF: the document, shown with its scripts off. */
.hp-frame { width: 100%; height: 520px; border: 1px solid var(--line, #e5e7eb); border-radius: 8px; background: #fff; }

/* A tool with one pane (no before and after) uses the whole width for it. */
.ph-compare:has(> .ph-pane:only-child) { grid-template-columns: minmax(0, 1fr); }

/* Merge images */
/* The pictures, in order, as a strip under the preview. */
.mg-list { display: flex; gap: 8px; margin-top: 10px; padding-bottom: 6px; overflow-x: auto; scroll-snap-type: x proximity; }
.mg-row { flex: 0 0 112px; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 6px; border: 1px solid var(--line, #e5e7eb); border-radius: 8px; background: var(--surface, #fff); position: relative; scroll-snap-align: start; }
.mg-row canvas { width: 72px; height: 56px; object-fit: contain; background: repeating-conic-gradient(#e2e8f0 0 25%, #f8fafc 0 50%) 0 0 / 10px 10px; }
.mg-num { position: absolute; top: 4px; left: 6px; font-size: 11px; font-weight: 700; color: var(--muted, #64748b); }
.mg-name { width: 100%; font-size: 11.5px; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mg-size { font-size: 11px; color: var(--muted, #64748b); }
.mg-btns { display: flex; gap: 3px; }
.mg-btns .btn { padding: 2px 8px; min-width: 0; }
/* The drop area becomes the viewer once there are pictures. */
.mg-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-bottom: 10px; }
.mg-bar .ph-info { margin: 0 0 0 auto; font-size: 13px; }
.mg-pane.is-over .mg-viewer { outline: 2px dashed var(--accent); outline-offset: 4px; border-radius: 8px; }
.merge-tool .mg-drop { min-height: 320px; }
.mg-preview-box { width: fit-content; max-width: 100%; margin: 0 auto; }
.mg-preview { display: block; max-width: 100%; height: auto; margin: 0 auto; border: 1px solid var(--line, #e5e7eb); background: repeating-conic-gradient(#e2e8f0 0 25%, #f8fafc 0 50%) 0 0 / 16px 16px; }
.mg-trace-row { display: flex; gap: 6px; margin-top: 8px; align-items: stretch; }
.mg-trace-row select { flex: 0 0 auto; width: auto; height: auto; }
.mg-trace-row .btn { flex: 1 1 auto; width: auto; margin: 0; }
.merge-tool .ph-row input[type="number"] { width: 90px; }

/* Watermark remover */
.rm-stage { position: relative; line-height: 0; margin: 0 auto; background: repeating-conic-gradient(#e2e8f0 0 25%, #f8fafc 0 50%) 0 0 / 16px 16px; border: 1px solid var(--line, #e5e7eb); border-radius: 8px; overflow: hidden; }
.rm-stage canvas { display: block; width: 100%; height: auto; }
.rm-stage #rmOverlay { position: absolute; inset: 0; width: 100%; height: 100%; cursor: none; touch-action: none; }
.rm-box { position: absolute; border: 2px dashed #ef4444; background: rgba(239, 68, 68, 0.2); pointer-events: none; }
.rm-cursor { position: absolute; border: 2px solid #fff; outline: 1px solid rgba(0, 0, 0, 0.6); border-radius: 50%; pointer-events: none; }
.rm-progress { width: 100%; height: 8px; margin-top: 8px; }
.rm-tools { display: flex; margin-bottom: 12px; }
.rm-tools button { flex: 1; }
.rm-row { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 4px; }
.rm-reset { margin-top: 8px; }

/* Web page converters */
.wc-url { display: grid; gap: 6px; margin-bottom: 12px; }
.wc-url textarea { width: 100%; font: 14px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; padding: 8px; border: 1px solid var(--line, #e5e7eb); border-radius: 8px; background: var(--surface, #fff); color: inherit; resize: vertical; }
.wc-url .btn { justify-self: start; }
.wc-paste { margin: 10px 0; }
.wc-paste summary { cursor: pointer; font-weight: 600; font-size: 14px; }
.wc-paste[open] { display: grid; gap: 6px; }
.wc-paste textarea { width: 100%; font: 13px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; padding: 8px; border: 1px solid var(--line, #e5e7eb); border-radius: 8px; background: var(--surface, #fff); color: inherit; resize: vertical; }
.wc-paste .btn { justify-self: start; }
.wc-nourl { margin: 10px 0 0; }
.wc-list { display: grid; gap: 6px; margin-top: 12px; }
.wc-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px 10px; border: 1px solid var(--line, #e5e7eb); border-radius: 8px; background: var(--surface, #fff); }
.wc-name { flex: 1 1 180px; min-width: 0; font-size: 14px; overflow-wrap: anywhere; }
.wc-status { font-size: 13px; color: var(--muted, #64748b); }
.wc-row[data-status="done"] .wc-status { color: #15803d; }
.wc-row[data-status="failed"] .wc-status { color: #b91c1c; }
.wc-row[data-status="working"] .wc-status::before { content: ""; display: inline-block; width: 10px; height: 10px; margin-right: 6px; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: wc-spin 0.8s linear infinite; vertical-align: -1px; }
@keyframes wc-spin { to { transform: rotate(360deg); } }
.wc-act { display: flex; gap: 4px; }
.wc-clear { margin-top: 8px; }

/* Every result preview carries the mark; the download is the file. */
.is-marked-preview { position: relative; }
.is-marked-preview::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 2;
  /* Two marks per tile, staggered like brickwork, so the rows interlock and
     there is no clear band anywhere across the picture. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='116'%3E%3Cg font-family='sans-serif' text-anchor='middle' fill='%23ffffff' fill-opacity='.6' stroke='%23000000' stroke-opacity='.3' stroke-width='.9'%3E%3Cg transform='rotate(-30 45 34)'%3E%3Ctext x='45' y='33' font-size='19' font-weight='700'%3EPREVIEW%3C/text%3E%3Ctext x='45' y='50' font-size='11' font-weight='600'%3Eveconvert.com%3C/text%3E%3C/g%3E%3Cg transform='rotate(-30 135 92)'%3E%3Ctext x='135' y='91' font-size='19' font-weight='700'%3EPREVIEW%3C/text%3E%3Ctext x='135' y='108' font-size='11' font-weight='600'%3Eveconvert.com%3C/text%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
  background-size: 180px 116px;
}

/* ------------------------------------------------------------ CAD viewer ----
 * A drawing looked round: a toolbar over the canvas, layers in the side
 * panel, the measure result floating over the drawing.
 */
.cadv-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
}
.cadv-group { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.cadv-bar .btn[aria-pressed="true"].is-active { background: var(--accent); color: #fff; border-color: var(--accent); }
.cadv-bar .btn.is-off { opacity: 0.55; }
.cadv-coords {
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  min-width: 11em;
  text-align: right;
  white-space: pre;
}
.cadv-stage { height: clamp(420px, 66vh, 820px); background: #1e1f22; }
#cadCanvas { display: block; width: 100%; height: 100%; touch-action: none; cursor: grab; }
#cadCanvas:active { cursor: grabbing; }
.cadv-help {
  position: absolute;
  left: 10px;
  bottom: 8px;
  margin: 0;
  font-size: 12px;
  color: #cbd5e1;
  background: rgba(15, 23, 42, 0.55);
  padding: 3px 8px;
  border-radius: 6px;
  pointer-events: none;
}
.cadv-measure {
  position: absolute;
  top: 10px;
  left: 10px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 10px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  border: 1px solid var(--border);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.cadv-measure b { font-size: 16px; }
.cadv-measure span { color: var(--muted); font-size: 12px; }
.cadv-h { margin-top: 18px !important; display: flex; justify-content: space-between; align-items: baseline; }
.cadv-layer-all { text-transform: none; letter-spacing: 0; font-size: 12px; }
.linklike { background: none; border: 0; padding: 0; color: var(--accent); cursor: pointer; font: inherit; text-decoration: underline; }
.cadv-layers { list-style: none; margin: 0; padding: 0; max-height: 320px; overflow-y: auto; }
.cadv-layers li label {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
  padding: 5px 2px;
  font-size: 13px;
  cursor: pointer;
  border-bottom: 1px solid var(--border);
}
.cadv-swatch { width: 12px; height: 12px; border-radius: 3px; border: 1px solid rgba(0, 0, 0, 0.35); }
.cadv-lname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cadv-count { color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; }
.cadv-convert { display: flex; flex-wrap: wrap; gap: 6px; }
@media (max-width: 900px) {
  .cadv-stage { height: 60vh; min-height: 340px; }
  .cadv-coords { display: none; }
}

/* Layout styles of the merge and grid pages: a drawing of each. */
.mg-styles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(76px, 1fr));
  gap: 6px;
  margin-bottom: 12px;
}
.mg-style {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 7px 4px 6px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 11.5px;
  line-height: 1.2;
  cursor: pointer;
}
.mg-style svg { width: 44px; height: 34px; }
.mg-style svg rect { fill: color-mix(in srgb, var(--muted) 45%, transparent); }
.mg-style:hover { border-color: var(--accent); }
.mg-style.is-on { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent) inset; }
.mg-style.is-on svg rect { fill: var(--accent); }

/* Embroidery viewer: the sew-out bar under the design, the fabric picker. */
.embv-stage { background: #ffffff; }
.embv-play {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-top: 1px solid var(--border);
}
.embv-play input[type="range"] { flex: 1; min-width: 0; }
.embv-count { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.embv-fabric { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; }
.embv-fabric input { width: 30px; height: 26px; padding: 0; border: 1px solid var(--border); border-radius: 6px; background: none; }
.embv-colors { list-style: none; }
.fact dd .muted { color: var(--muted); font-size: 12px; }
#embCanvas { display: block; width: 100%; height: 100%; touch-action: none; cursor: grab; }
#embCanvas:active { cursor: grabbing; }

/* Merge / grid styles, in groups; social sizes as cards with a name and size. */
.mg-group { margin: 12px 0 6px; font-size: 0.78rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.mg-presets { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 6px; margin-bottom: 4px; }
.mg-style.mg-preset { flex-direction: row; justify-content: flex-start; gap: 10px; padding: 8px 10px; text-align: left; }
.mg-preset svg { flex: none; width: 36px; height: 28px; }
.mg-preset span { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.mg-preset strong { font-size: 0.86rem; }
.mg-preset small { font-size: 0.74rem; color: var(--muted); }

/* Grid maker: the controls stay beside the preview, their groups in tabs,
 * the finer settings folded, and the download button always in reach. */
.merge-tool { overflow: clip; }
.merge-tool .photo-work { grid-template-columns: minmax(0, 1fr) 330px; align-items: start; }
.merge-tool .ph-controls { position: sticky; top: 0; max-height: 100vh; overflow-y: auto; padding-bottom: 0; }
.mg-tabs { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 8px; }
.mg-tab { flex: 1 1 auto; padding: 6px 8px; border: 1px solid var(--border); border-radius: 999px; background: var(--surface); color: var(--text); font: inherit; font-size: 12.5px; cursor: pointer; white-space: nowrap; }
.mg-tab:hover { border-color: var(--accent); }
.mg-tab.is-on { background: var(--accent); border-color: var(--accent); color: #fff; }
.mg-tabpanel { margin-bottom: 12px; }
.merge-tool .mg-presets { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.merge-tool .mg-style.mg-preset { gap: 6px; padding: 6px 7px; }
.merge-tool .mg-preset svg { width: 26px; height: 22px; }
.merge-tool .mg-preset strong { font-size: 0.78rem; }
.merge-tool .mg-preset small { font-size: 0.68rem; }
.mg-more { margin: 4px 0 14px; border: 1px solid var(--border); border-radius: 8px; padding: 0 10px; }
.mg-more > summary { padding: 9px 0; font-size: 13px; font-weight: 600; cursor: pointer; }
.mg-more[open] > summary { margin-bottom: 6px; }
.mg-actions { position: sticky; bottom: 0; margin: 0 -14px; padding: 10px 14px 12px; background: var(--surface); border-top: 1px solid var(--border); }
@media (max-width: 860px) {
  .merge-tool .photo-work { grid-template-columns: minmax(0, 1fr); }
  .merge-tool .ph-controls { position: static; max-height: none; overflow: visible; }
  .merge-tool .mg-drop { min-height: 200px; }
}

/* Embroidery pages: the words people search by, marked. */
strong.kw { font-weight: 700; color: inherit; background: color-mix(in srgb, var(--accent) 12%, transparent); padding: 0 3px; border-radius: 3px; }
.kw-also { display: flex; flex-wrap: wrap; gap: 6px 14px; list-style: none; margin: 8px 0 0; padding: 0; }
.kw-also li { font-size: 14px; color: var(--muted); }
.kw-also li::before { content: '\2192'; margin-right: 6px; color: var(--accent); }
mark.kw { background: color-mix(in srgb, var(--accent) 16%, transparent); color: inherit; padding: 0 3px; border-radius: 3px; font-weight: 600; }
.kw-tags { display: flex; flex-wrap: wrap; gap: 6px; list-style: none; margin: 12px 0 16px; padding: 0; }
.kw-tags li { padding: 4px 10px; border-radius: 999px; background: color-mix(in srgb, var(--accent) 12%, transparent); border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent); font-size: 13px; font-weight: 600; }
.kw-steps { padding-left: 1.3em; }
.kw-steps li { margin-bottom: 6px; }

/* Photo to sketch: the styles as presets, each drawn from the photo. */
.sk-head { margin: 4px 0 8px; font-size: 12px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
.sk-styles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; margin-bottom: 14px; }
.sk-style { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 5px 4px 6px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--text); font: inherit; font-size: 11.5px; line-height: 1.2; cursor: pointer; text-align: center; }
.sk-style canvas { width: 100%; max-width: 92px; aspect-ratio: 1; border-radius: 5px; background: repeating-linear-gradient(45deg, #eef1f5 0 6px, #e2e7ee 6px 12px); }
.sk-style:hover { border-color: var(--accent); }
.sk-style.is-on { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent) inset; }

/* The cut-ready ZIP's preview: what is inside it. */
.zip-list { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 14px; line-height: 1.9; margin: 24px; }

/* Trade hubs (svg-for-cricut and siblings). */
.hub-features, .hub-projects { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px 20px; }
.hub-feature h3, .hub-projects h3, .hub-brand h3 { margin: 0 0 4px; font-size: 15px; }
.hub-feature p, .hub-projects p { margin: 0; color: var(--muted); font-size: 14px; line-height: 1.5; }
.hub-feature h3::before { content: ""; display: inline-block; width: 8px; height: 8px; margin-right: 8px; border-radius: 50%; background: var(--accent); vertical-align: 2px; }
.hub-machines { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; }
.hub-brand { border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px; }
.hub-brand ul { margin: 0; padding-left: 18px; color: var(--muted); font-size: 14px; line-height: 1.7; }
.emb-formats { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 8px; margin-top: 8px; }
.emb-format { display: grid; gap: 2px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; }
.emb-format b { font-size: 15px; }
.emb-format small { color: var(--muted); font-size: 12.5px; }
.emb-ext { justify-self: start; font: 700 11px/1 ui-monospace, Menlo, Consolas, monospace; letter-spacing: 0.06em; padding: 4px 6px; border-radius: 4px; background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent); }

/* Footer: the converter in each language, and the trade hubs. */
.foot-langs { border-top: 1px solid var(--border); padding-top: 18px; margin-top: 8px; }
.foot-langs h3 { margin: 0 0 8px; font-size: 13px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
.foot-langs p { display: flex; flex-wrap: wrap; gap: 6px 18px; margin: 0 0 10px; }
.foot-langs a { color: inherit; font-size: 14px; }
.foot-langs a span { color: var(--muted); font-size: 12px; }
[dir="rtl"] .crumb, [dir="rtl"] .hero { text-align: right; }

/* ---- Data tools: PDF/Excel/CSV/JSON, charts, analysis, compare ---- */
.data-tool { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); margin-bottom: 28px; overflow: hidden; }
.dt-grid { display: grid; grid-template-columns: minmax(0, 1fr) 300px; }
.dt-main { padding: 16px; min-width: 0; }
.dt-main > h3, .dt-main h3 { margin: 18px 0 8px; font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.dt-input .drop { min-height: 150px; }
.dt-status { margin: 8px 0 0; font-size: 13px; color: var(--muted); min-height: 1em; }
.dt-sheets label { font-size: 13px; display: inline-flex; gap: 8px; align-items: center; margin-top: 6px; }
.dt-alt { margin-top: 8px; font-size: 14px; }
.dt-alt summary { cursor: pointer; color: var(--accent); font-weight: 600; }
.dt-alt textarea { width: 100%; margin: 8px 0; font: 13px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; padding: 8px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2); color: var(--text); resize: vertical; }
.dt-link { display: flex; gap: 8px; margin-top: 8px; }
.dt-link input { flex: 1; min-width: 0; padding: 7px 9px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--text); }
.dt-preview { margin-top: 14px; }
.dt-table-wrap { overflow: auto; max-height: 440px; border: 1px solid var(--border); border-radius: 8px; }
.dt-preview-small .dt-table-wrap { max-height: 220px; }
.dt-table { border-collapse: collapse; width: 100%; font-size: 13px; font-variant-numeric: tabular-nums; }
.dt-table th, .dt-table td { padding: 6px 10px; border-bottom: 1px solid var(--border); text-align: left; white-space: nowrap; max-width: 320px; overflow: hidden; text-overflow: ellipsis; vertical-align: top; }
.dt-table thead th { position: sticky; top: 0; background: var(--surface-2); font-weight: 650; z-index: 1; }
.dt-table td.num { text-align: right; }
.dt-table .dt-rn { color: var(--muted); width: 1%; text-align: right; }
.dt-type { display: block; font-weight: 400; font-size: 11px; color: var(--muted); }
.dt-out { margin: 0; max-height: 260px; overflow: auto; padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2); font: 12.5px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: pre; }
.dt-actions { display: flex; gap: 8px; margin-top: 8px; flex-wrap: wrap; }
.dt-actions .btn { flex: 1; }
.dt-actions-row { margin: 12px 0; }
.dt-actions-row .btn { flex: 0 1 auto; }
.dt-sample { margin-top: 14px; width: 100%; }
.ph-controls input[type=text], .ph-controls input:not([type]) { flex: 1; min-width: 0; padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); color: var(--text); }
.ph-controls fieldset { border: 0; padding: 0; margin: 0 0 12px; }
.ph-controls legend { font-size: 13px; font-weight: 600; margin-bottom: 6px; }
.ch-ys { max-height: 200px; overflow: auto; }
.ch-colors { display: grid; gap: 6px; margin-bottom: 12px; }
.ch-color { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.ch-color input { width: 32px; height: 24px; padding: 0; border: 1px solid var(--border); border-radius: 4px; background: none; }
.dt-chart-stage { margin-top: 14px; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; line-height: 0; }
.dt-chart-stage:empty { display: none; }
.dt-chart-stage svg { width: 100%; height: auto; display: block; }
#dtChartData details { margin-top: 10px; font-size: 14px; }
#dtChartData summary { cursor: pointer; color: var(--accent); font-weight: 600; margin-bottom: 8px; }
.dt-facts { margin: 14px 0; }
.dt-facts ul { margin: 0; padding-left: 20px; }
.dt-facts li { margin: 4px 0; }
.dt-charts { display: grid; gap: 14px; }
.dt-chart-fig { margin: 0; }
.dt-chart-fig figcaption { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px; align-items: center; margin-top: 6px; font-size: 13px; color: var(--muted); }
.dt-dl { display: inline-flex; gap: 6px; }
.dt-note { margin: 8px 16px; font-size: 14px; }
.dt-note.warn { color: var(--warn); }
.cmp-inputs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.cmp-inputs .dt-input h3 { margin-top: 0; }
.cmp-summary { margin: 16px 0 10px; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.cmp-summary .hint { flex-basis: 100%; margin: 4px 0 0; }
.cmp-match { flex-basis: 100%; margin: 0 0 4px; color: var(--ok); }
.chip { display: inline-block; padding: 4px 10px; border-radius: 999px; font-size: 13px; font-weight: 600; background: var(--surface-2); border: 1px solid var(--border); }
.chip-chg { background: #fff7d6; border-color: #f0d67a; color: #6b5200; }
.chip-add { background: #e3f6ec; border-color: #9fd9b9; color: #0f5b33; }
.chip-del { background: #fde8e8; border-color: #f3b0b0; color: #8a1c1c; }
.cmp-table td.add { background: #e9f8ef; }
.cmp-table td.del { background: #fdeeee; text-decoration: line-through; text-decoration-color: rgba(138, 28, 28, .45); }
.cmp-table td.chg { background: #fff8dc; }
.cmp-table td.chg del { color: #8a1c1c; margin-right: 6px; }
.cmp-table td.chg ins { color: #0f5b33; text-decoration: none; font-weight: 650; }
.cmp-kind { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; padding: 2px 6px; border-radius: 4px; }
.cmp-changed { background: #fff1b8; color: #6b5200; }
.cmp-added { background: #c9efd9; color: #0f5b33; }
.cmp-removed { background: #f8cccc; color: #8a1c1c; }
.cmp-same { color: var(--muted); }
:root[data-theme="dark"] .chip-chg, :root[data-theme="dark"] .cmp-table td.chg { background: #3a3210; color: #f5dc80; border-color: #6b5a1a; }
:root[data-theme="dark"] .chip-add, :root[data-theme="dark"] .cmp-table td.add { background: #12331f; color: #9be3b8; border-color: #245a3a; }
:root[data-theme="dark"] .chip-del, :root[data-theme="dark"] .cmp-table td.del { background: #3a1616; color: #f3aaaa; border-color: #6b2a2a; }
:root[data-theme="dark"] .cmp-table td.chg del { color: #f3aaaa; }
:root[data-theme="dark"] .cmp-table td.chg ins { color: #9be3b8; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .chip-chg, :root:not([data-theme="light"]) .cmp-table td.chg { background: #3a3210; color: #f5dc80; border-color: #6b5a1a; }
  :root:not([data-theme="light"]) .chip-add, :root:not([data-theme="light"]) .cmp-table td.add { background: #12331f; color: #9be3b8; border-color: #245a3a; }
  :root:not([data-theme="light"]) .chip-del, :root:not([data-theme="light"]) .cmp-table td.del { background: #3a1616; color: #f3aaaa; border-color: #6b2a2a; }
  :root:not([data-theme="light"]) .cmp-table td.chg del { color: #f3aaaa; }
  :root:not([data-theme="light"]) .cmp-table td.chg ins { color: #9be3b8; }
}
@media (max-width: 860px) {
  .dt-grid { grid-template-columns: 1fr; }
  .dt-grid .ph-controls { border-left: 0; border-top: 1px solid var(--border); }
  .cmp-inputs { grid-template-columns: 1fr; }
}

/* ---- Password dialog (PDF and Excel files that are locked) ---- */
.pw-dialog { border: 1px solid var(--border); border-radius: var(--radius); padding: 0; width: min(420px, calc(100vw - 32px)); background: var(--surface); color: var(--text); box-shadow: var(--shadow); }
.pw-dialog::backdrop { background: rgba(15, 23, 42, .45); }
.pw-form { padding: 20px; display: grid; gap: 10px; }
.pw-title { margin: 0; font-size: 18px; }
.pw-file { margin: 0; color: var(--muted); font-size: 14px; word-break: break-all; }
.pw-label { display: grid; gap: 6px; font-weight: 600; font-size: 14px; }
.pw-label input { padding: 9px 10px; border: 1px solid var(--border); border-radius: 8px; font-size: 15px; background: var(--surface); color: var(--text); }
.pw-show { font-size: 13px; display: flex; gap: 6px; align-items: center; }
.pw-error { margin: 0; color: var(--danger); font-size: 14px; }
.pw-hint { margin: 0; color: var(--muted); font-size: 12.5px; }
.pw-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; }

/* Tattoo stencil colour chips */
#phTatInkSeg { display: flex; gap: 6px; flex-wrap: wrap; }
.tat-ink {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  padding: 6px 10px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface); font-size: 13px; min-height: 36px;
}
.tat-ink input { position: absolute; opacity: 0; pointer-events: none; }
.tat-ink:has(input:checked) { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.tat-ink:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.tat-dot { width: 14px; height: 14px; border-radius: 50%; display: inline-block; }
.ph-row .ph-label { flex: 0 0 84px; font-size: 13px; }
.ph-row .tat-ink { flex: 0 0 auto; }

/* GeoJSON converter */
.geo-map {
  margin: 14px 0 10px; border: 1px solid var(--border); border-radius: var(--radius);
  background: #f5f8fc; overflow: hidden; position: relative;
}
.geo-map svg { display: block; width: 100%; height: auto; max-height: 520px; }
:root[data-theme="dark"] .geo-map { background: #1c2129; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .geo-map { background: #1c2129; } }
.geo-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 8px 16px; margin: 0 0 12px; }
.geo-facts div { min-width: 0; }
.geo-facts dt { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.geo-facts dd { margin: 2px 0 0; font-size: 14px; overflow-wrap: anywhere; }
.geo-tag { display: inline-block; font-size: 11px; padding: 1px 7px; border-radius: 999px; background: var(--accent-soft); color: var(--accent); margin-left: 4px; }
.geo-tag-warn { background: var(--warn-soft); color: var(--warn); }

/* Text to engraving path: the typing panel */
.hershey-panel { margin-bottom: 14px; }
.hershey-title { margin: 0 0 8px; font-size: 16px; }
.hershey-panel textarea { width: 100%; font: 16px/1.4 system-ui, sans-serif; padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--text); resize: vertical; }
.hershey-row { display: flex; flex-wrap: wrap; gap: 10px 18px; margin: 10px 0; font-size: 13px; }
.hershey-row label { display: flex; align-items: center; gap: 6px; }
.hershey-row select, .hershey-row input[type=number] { padding: 5px 7px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); color: var(--text); }
.hershey-row input[type=number] { width: 72px; }
.hershey-preview { border: 1px dashed var(--border); border-radius: 8px; background: #fff; min-height: 60px; padding: 8px; }
.hershey-preview svg { display: block; width: 100%; height: auto; max-height: 260px; }
.hershey-preview .et-canvas { display: block; max-width: 100%; max-height: 220px; margin: 0 auto; }
#etInfo.is-warn { color: var(--warn, #b45309); }
.hershey-row input[type=color] { width: 36px; height: 28px; padding: 0 2px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); }

/* The tip that the preview mark is not in the download. */
/* Converter result: the original and the result side by side. */
.cmp-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; align-items: start; }
.cmp-pair.is-single { grid-template-columns: 1fr; }
.cmp-pane { margin: 0; min-width: 0; position: relative; }
.cmp-pane img { display: block; width: 100%; height: auto; max-height: 520px; object-fit: contain; border-radius: var(--radius, 10px); }
.cmp-pane figcaption { text-align: center; font-size: 13px; color: var(--muted, #6b7280); margin-top: 6px; }
@media (max-width: 640px) { .cmp-pair { grid-template-columns: 1fr; } }
/* An information note shown as a pop-up; kept in the page out of sight. */
.note.note-popped { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* Pop-up tips stack at the foot of the screen, newest at the bottom. */
.tip-stack {
  position: fixed; left: 16px; right: 16px; bottom: 20px; z-index: 200;
  display: flex; flex-direction: column; align-items: flex-end; gap: 8px; pointer-events: none;
}
.tip-stack .mark-tip { position: static; }
/* A chat message, as WhatsApp draws one: a green bubble with a tail at its
   bottom corner, stacked in the corner like a conversation. */
.mark-tip {
  --bubble: #d9fdd3; --bubble-ink: #111b21; --bubble-mark: #1fa855;
  position: fixed; right: 16px; bottom: 20px; z-index: 200;
  width: fit-content; max-width: min(420px, calc(100vw - 32px));
  transform: translateY(12px) scale(.96); transform-origin: bottom right; opacity: 0;
  transition: opacity .25s ease, transform .25s ease;
  display: flex; align-items: flex-start; gap: 8px;
  padding: 8px 8px 8px 12px; border-radius: 12px 12px 3px 12px;
  background: var(--bubble); color: var(--bubble-ink); font-size: 14px; line-height: 1.45;
  box-shadow: 0 1px 1px rgba(11, 20, 26, .13), 0 6px 18px rgba(11, 20, 26, .12);
  /* It floats over the tool, so it must never take a click or a drag meant
     for what is underneath: only its close button is live. */
  pointer-events: none;
}
.mark-tip::after {
  content: ''; position: absolute; right: -7px; bottom: 0; width: 12px; height: 12px;
  background: var(--bubble); clip-path: polygon(0 0, 0 100%, 100% 100%);
}
.mark-tip.is-in { opacity: 1; transform: none; }
.mark-tip svg { flex: none; margin-top: 1px; color: var(--bubble-mark); }
.mark-tip b { color: var(--bubble-mark); }
.mark-tip-x {
  pointer-events: auto;
  flex: none; margin: -2px 0 0 2px; width: 24px; height: 24px; border: 0; border-radius: 50%;
  background: transparent; color: #54656f; font-size: 18px; line-height: 1; cursor: pointer;
}
.mark-tip-x:hover { background: rgba(11, 20, 26, .08); color: #111b21; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .mark-tip { --bubble: #005c4b; --bubble-ink: #e9edef; --bubble-mark: #7ee2b8; }
  :root:not([data-theme="light"]) .mark-tip-x { color: #aebac1; }
}
:root[data-theme="dark"] .mark-tip { --bubble: #005c4b; --bubble-ink: #e9edef; --bubble-mark: #7ee2b8; }
:root[data-theme="dark"] .mark-tip-x { color: #aebac1; }
@media (max-width: 560px) { .tip-stack { align-items: stretch; } .mark-tip { max-width: none; } }
@media (prefers-reduced-motion: reduce) { .mark-tip { transition: none; } }

/* Grids: arrange photos on the preview */
.mg-preview { touch-action: none; cursor: grab; }
.mg-preview:active { cursor: grabbing; }
.mg-arrange-hint { margin: 6px 0 0; }
.mg-zoom-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 8px 0; font-size: 13px; }
.mg-zoom-row input[type=range] { flex: 1 1 140px; min-width: 120px; }
.mg-zoom-name { flex: 1 1 100%; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mg-dl-row { display: flex; gap: 8px; }
.mg-dl-row #mgDownload { flex: 1; }
@media (max-width: 720px) { .mg-dl-row { flex-direction: column; } }

/* A PDF open in a drawing tool: its page picker and the all-pages button. */
.ph-pdfbar { display: grid; gap: 8px; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line, rgba(0,0,0,.1)); }
.ph-pdfbar[hidden] { display: none; }

/* KDP cover converter: the sheet preview, scaled to the pane. */
.kc-preview { display: grid; place-items: center; padding: 8px; background: var(--surface-2, #f3f4f6); border-radius: 10px; }
.kc-preview[hidden] { display: none; }
.kc-canvas { max-width: 100%; height: auto; box-shadow: 0 2px 10px rgba(0,0,0,.18); }
.kc-tool textarea { width: 100%; font: inherit; }
.kc-blank { display: block; margin: 8px auto 0; }

/* AI images to KDP: the batch list. */
.ki-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; max-height: 520px; overflow: auto; }
.ki-list[hidden] { display: none; }
.ki-item { display: grid; grid-template-columns: 96px 1fr; gap: 12px; align-items: center; padding: 8px; border: 1px solid var(--line, rgba(0,0,0,.1)); border-radius: 10px; background: var(--surface, #fff); }
.ki-thumb { width: 96px; height: 96px; display: grid; place-items: center; background: #fff; border-radius: 6px; }
.ki-thumb canvas { max-width: 96px; max-height: 96px; box-shadow: 0 1px 4px rgba(0,0,0,.15); }
.ki-meta { display: grid; gap: 2px; font-size: .9rem; min-width: 0; overflow-wrap: anywhere; }
.ki-err { color: #b91c1c; }

/* Tattoo stencil: size and cut-line inputs, and the any-colour swatch. */
.tat-size { display: inline-flex; gap: 6px; align-items: center; }
.tat-size input[type="number"] { width: 72px; }
.tat-ink input[type="color"] { width: 22px; height: 22px; padding: 0; border: 0; border-radius: 50%; background: none; cursor: pointer; }

/* The comparison table at the foot of a converter page: smaller, quieter. */
.cmp-compact h2 { font-size: 1.05rem; }
.cmp-compact .cmp-table { font-size: 12.5px; min-width: 560px; }
.cmp-compact .cmp-table th, .cmp-compact .cmp-table td { padding: 6px 8px; }
.ph-frame.is-pickable, .cmp-pane.is-pickable { cursor: crosshair; }

/* XML / JSON / YAML converter */
.tr-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; padding: 16px; }
.tr-pane { min-width: 0; display: flex; flex-direction: column; }
.tr-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 8px; }
.tr-head label { font-size: 13px; font-weight: 600; }
.tr-head .tr-open { margin-left: auto; cursor: pointer; }
.tr-text { width: 100%; min-height: 360px; resize: vertical; font: 13px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; padding: 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: inherit; tab-size: 2; white-space: pre; overflow: auto; }
.tr-text.is-error { border-color: #c0392b; }
.tr-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; padding: 0 16px 16px; }
.tr-bar .ph-row { margin: 0; }
@media (max-width: 760px) { .tr-grid { grid-template-columns: 1fr; } .tr-text { min-height: 220px; } }
.mz-view { border-radius: 10px; overflow: hidden; margin: 12px 0; background: #eef1f5; min-height: 40px; }

/* Document converter */
.dc-frame { border: 1px solid var(--border); border-radius: 8px; background: #fff; overflow: hidden; min-height: 360px; display: flex; }
.dc-preview { flex: 1; width: 100%; min-height: 420px; border: 0; background: #fff; }
.dc-drop { display: flex; align-items: center; justify-content: center; min-height: 360px; text-align: center; cursor: pointer; border: 2px dashed var(--border); border-radius: 8px; padding: 24px; }
.dc-drop.is-over, .doc-tool .is-over { border-color: var(--accent, #0a5bc0); }
.dc-text { white-space: pre-wrap; }
.dc-outwrap { padding: 0 16px 16px; }
.dc-outwrap .tr-text { min-height: 220px; }
@media (max-width: 760px) { .dc-preview { min-height: 320px; } .dc-drop { min-height: 180px; } }

/* Another file, once one is open: under the original, and the whole tool takes a drop. */
.ph-another { margin-top: 8px; cursor: pointer; }
.ph-regen { margin-top: 8px; display: inline-flex; align-items: center; gap: 5px; }
#doneRegen { display: inline-flex; align-items: center; gap: 5px; }
#doneRegen[hidden], .ph-regen[hidden] { display: none; }
.photo-work.is-over { outline: 2px dashed var(--accent, #2563eb); outline-offset: 6px; border-radius: 12px; }
.tool-another { margin: 0 0 12px; cursor: pointer; }

/* Comparison: the first differences on the page, the rest in a download. */
.cmp-showing { font-size: 13px; color: var(--muted, #5d6763); margin: 6px 0; }
.cmp-more-row td { padding: 0 !important; border: 0; }
.cmp-more { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 16px; background: linear-gradient(to bottom, rgba(255,255,255,0), var(--accent-soft, #eff6ff) 40%); border-top: 1px dashed var(--border, #d6dbe3); }
.cmp-more p { margin: 0; }
.cmp-lines td.ln { width: 3.5em; text-align: right; color: var(--muted, #5d6763); font: 12px ui-monospace, Menlo, monospace; }
.cmp-lines code { white-space: pre-wrap; word-break: break-word; }
.cmp-gap td { text-align: center; color: var(--muted, #5d6763); background: transparent; }
.cmp-src { max-height: 220px; overflow: auto; font-size: 12px; }
.cmp-tree-only { display: none; }
[data-cmp="tree"] .cmp-tree-only { display: flex; }
[data-cmp="tree"] .cmp-ignore, [data-cmp="text"] .cmp-ignore, [data-cmp="text"] #cmpNum, [data-cmp="tree"] label:has(#cmpOnly), [data-cmp="text"] label:has(#cmpOnly) { display: none; }
.cmp-dialog { border: 0; border-radius: 14px; padding: 0; max-width: min(560px, calc(100vw - 32px)); width: 100%; box-shadow: 0 20px 60px rgba(15, 23, 42, .3); }
.cmp-dialog::backdrop { background: rgba(15, 23, 42, .45); }
.cmp-dialog-body { display: grid; gap: 14px; padding: 22px; }
.cmp-dialog-body h3 { margin: 0; }
.cmp-dialog-body p { margin: 0; }
.cmp-formats { display: grid; gap: 8px; }
.cmp-formats .btn { display: grid; justify-content: stretch; justify-items: start; text-align: left; gap: 2px; padding: 10px 14px; width: 100%; }
.cmp-formats .btn span { font-size: 12px; font-weight: 400; opacity: .8; }
.cmp-dialog-body [data-close] { justify-self: end; }

/* Tattoo try-on: the body part, and its controls beside it. */
.tm-wrap { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 18px; align-items: start; }
.tm-stage canvas { width: 100%; height: auto; display: block; border-radius: var(--radius, 10px); background: #eef0f3; touch-action: none; cursor: grab; }
.tm-side h3 { font-size: 13px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted, #6b7280); margin: 14px 0 8px; }
.tm-side h3:first-child { margin-top: 0; }
.tm-ratios, .tm-skins { display: flex; flex-wrap: wrap; gap: 6px; }
.tm-chip { padding: 6px 12px; border-radius: 999px; border: 1px solid var(--line, #d7dce3); background: var(--card, #fff); font: inherit; font-size: 13px; cursor: pointer; }
.tm-chip.is-on, .tm-part.is-on { border-color: var(--accent, #6d3fc0); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent, #6d3fc0) 25%, transparent); }
.tm-parts { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 6px; }
.tm-part { display: grid; justify-items: center; gap: 2px; padding: 6px 4px; border-radius: 10px; border: 1px solid var(--line, #d7dce3); background: var(--card, #fff); font: inherit; font-size: 12px; cursor: pointer; }
.tm-part canvas { width: 45px; height: 55px; }
.tm-skin { width: 30px; height: 30px; border-radius: 50%; border: 2px solid transparent; cursor: pointer; }
.tm-skin.is-on { border-color: var(--accent, #6d3fc0); }
.tm-own { margin-top: 12px; cursor: pointer; }
@media (max-width: 760px) { .tm-wrap { grid-template-columns: 1fr; } }

/* Admin: support tickets */
.ticket { margin: 0 0 12px; }
.ticket-head { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.ticket-reply { margin: 6px 0; padding: 6px 10px; border-left: 3px solid var(--accent, #6d3fc0); background: color-mix(in srgb, var(--accent, #6d3fc0) 6%, transparent); }
.ticket .field-row { display: flex; gap: 6px; margin-top: 8px; }
.ticket .ticket-text { flex: 1; min-width: 0; }

/* ---- PCB tools (pcbtool.js) ---- */
.pcb-stage-wrap { position: relative; margin: 12px 0; }
.pcb-zoom { display: flex; gap: 6px; align-items: center; justify-content: flex-end; margin-bottom: 6px; font-size: 13px; color: var(--muted, #667); }
.pcb-zoom span { min-width: 3.5em; text-align: center; font-variant-numeric: tabular-nums; }
.pcb-stage { height: min(70vh, 640px); border-radius: 12px; overflow: hidden; background: #2a2a2a; cursor: grab; touch-action: none; border: 1px solid var(--line, #dde); }
.pcb-stage:active { cursor: grabbing; }
.pcb-stage svg { width: 100%; height: 100%; display: block; }
.pcb-layers { display: grid; gap: 6px; margin-top: 12px; }
.pcb-layer { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: 6px 10px; align-items: center; padding: 8px 10px; border: 1px solid var(--line, #dde); border-radius: 10px; background: var(--card, #fff); }
.pcb-layer b { word-break: break-all; font-weight: 600; }
.pcb-layer small { grid-column: 1 / -1; color: var(--muted, #667); }
.pcb-layer.is-bad { border-color: #d33; }
.pcb-layer select { font-size: 13px; }
@media (max-width: 640px) {
  .pcb-layer { grid-template-columns: 1fr 1fr; }
  .pcb-layer label { grid-column: 1 / -1; }
  .pcb-stage { height: 56vh; }
}
.post-links a { font-weight: 600; }

/* ---- draw a signature / paste SVG code (drawpad.js) ---- */
.drop-alt { margin-top: 14px; text-align: left; }
.drop-alt summary { cursor: pointer; font-weight: 600; color: var(--accent, #2563eb); }
.drop-alt textarea { width: 100%; margin: 8px 0; font: 13px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; border: 1px solid var(--line, #dde); border-radius: 8px; padding: 8px; background: var(--card, #fff); color: inherit; }
.drawpad { margin-top: 16px; text-align: left; }
.drawpad-title { margin: 0 0 6px; font-size: 14px; }
.drawpad canvas { display: block; width: 100%; height: 200px; background: #fff; border: 1px solid var(--line, #dde); border-radius: 10px; touch-action: none; cursor: crosshair; }
.drawpad-bar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 8px; font-size: 13px; }
.drawpad-bar label { display: inline-flex; gap: 6px; align-items: center; }
.drawpad-bar #drawUse { margin-left: auto; }
.br-canvas { max-width: 100%; height: auto; box-shadow: 0 1px 6px rgba(0,0,0,.18); }
.br-tool .kc-preview { margin-bottom: 10px; }

/* ---------- Pricing: Yearly / Monthly, the offer banner, plan flag ---------- */
.period-toggle { display: inline-flex; gap: 4px; padding: 4px; margin: 0 0 16px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 999px; }
.period-toggle button { border: 0; background: transparent; color: var(--text); font: inherit; font-weight: 600; padding: 8px 16px; border-radius: 999px; cursor: pointer; }
.period-toggle button.is-on { background: var(--accent); color: var(--accent-ink); }
.period-toggle .pill { margin-left: 6px; }
.plan { position: relative; }
.plan-pick { border: 2px solid var(--accent); }
.plan-flag { position: absolute; top: -11px; left: 50%; transform: translateX(-50%); background: var(--accent); color: var(--accent-ink); font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; padding: 3px 10px; border-radius: 999px; white-space: nowrap; }
.offer-banner { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 10px 14px; margin: 0 0 14px; border: 1px solid #f5c542; background: var(--warn-soft); color: var(--text); border-radius: var(--radius); font-weight: 600; }
.offer-banner[hidden] { display: none; }
.offer-banner b { background: #facc15; color: #1f2937; padding: 3px 10px; border-radius: 6px; font-variant-numeric: tabular-nums; }

/* ---------- The offer pop-ups: after a download, and on the way out ---------- */
/* One solid pastel red, no gradients; dark red text on it (white on a pastel is hard to read). */
.offer-veil { --offer: #f4a3a3; --offer-hover: #ee9090; --offer-ink: #4a0e0e; --offer-soft: #fde8e8; --offer-line: #e88b8b; --offer-tick: #c24141; }
.offer-veil { position: fixed; inset: 0; z-index: 1000; display: grid; place-items: center; padding: 16px; background: rgba(15, 23, 42, .55); backdrop-filter: blur(3px); opacity: 0; transition: opacity .2s; }
.offer-veil.is-in { opacity: 1; }
.offer { position: relative; width: min(640px, 100%); max-height: calc(100vh - 32px); overflow: auto; background: var(--surface); color: var(--text); border-radius: 16px; box-shadow: 0 24px 64px rgba(0, 0, 0, .35); padding: 22px; transform: translateY(8px) scale(.98); transition: transform .2s; }
.offer-veil.is-in .offer { transform: none; }
.offer-x { position: absolute; top: 10px; right: 12px; border: 0; background: transparent; color: var(--muted); font-size: 26px; line-height: 1; cursor: pointer; z-index: 1; }
.offer h2 { margin: 6px 0 4px; text-align: center; font-size: 26px; letter-spacing: -.02em; }
.offer-lead { margin: 0 0 14px; text-align: center; color: var(--muted); }
.offer-clock { position: relative; display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 10px 12px 14px; margin: 18px 0 14px; border: 1px solid #f5c542; border-radius: 10px; background: var(--warn-soft); font-weight: 600; overflow: hidden; }
.offer-clock b { background: #facc15; color: #1f2937; padding: 3px 10px; border-radius: 6px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.offer-clock i { position: absolute; left: 0; bottom: 0; height: 4px; background: #f59e0b; transition: width 1s linear; }
.offer-toggle { display: flex; justify-content: center; gap: 4px; width: max-content; margin: 0 auto 14px; padding: 4px; background: var(--surface-2); border-radius: 999px; }
.offer-toggle button { border: 0; background: transparent; color: var(--text); font: inherit; font-weight: 600; padding: 7px 14px; border-radius: 999px; cursor: pointer; }
.offer-toggle button.is-on { background: var(--surface); box-shadow: var(--shadow); }
.offer-toggle span { margin-left: 4px; padding: 2px 7px; border-radius: 6px; background: var(--offer); color: var(--offer-ink); font-size: 12px; }
.offer-plans { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.offer-plans[hidden] { display: none; }
.offer-plan { position: relative; display: flex; flex-direction: column; gap: 4px; padding: 16px; border: 1px solid var(--border); border-radius: 12px; color: inherit; text-decoration: none; background: var(--surface); }
.offer-plan.is-pick { border: 2px solid var(--offer-line); background: var(--offer-soft); color: #1f2937; }
.offer-plan.is-pick .offer-sub, .offer-plan.is-pick .offer-price s { color: #7a4545; }
.offer-plan:hover { border-color: var(--offer-line); }
.offer-flag { position: absolute; top: 12px; right: 12px; font-size: 11px; font-weight: 700; text-transform: uppercase; color: var(--offer-ink); background: var(--offer); padding: 2px 8px; border-radius: 6px; }
.offer-plan-name { font-weight: 700; }
.offer-price { font-size: 26px; font-weight: 800; letter-spacing: -.02em; }
.offer-price s { font-size: 15px; font-weight: 600; color: var(--muted); margin-left: 4px; }
.offer-sub { font-size: 13px; color: var(--muted); }
.offer-vol { margin-top: 6px; }
.offer-li { font-size: 13px; padding-left: 18px; position: relative; }
.offer-li::before, .offer-list li::before { content: '✓'; position: absolute; left: 0; color: var(--offer-tick); font-weight: 700; }
.offer-btn { margin-top: 10px; text-align: center; padding: 10px; border-radius: 10px; font-weight: 700; border: 1px solid var(--border); }
.offer-plan.is-pick .offer-btn { background: var(--offer); color: var(--offer-ink); border-color: var(--offer-line); }
.offer-one { text-align: center; margin: 14px 0 4px; font-size: 14px; }
.offer-one a { color: var(--danger); font-weight: 600; }
.offer-no { display: block; margin: 6px auto 0; border: 0; background: transparent; color: var(--muted); font: inherit; font-size: 13px; cursor: pointer; text-decoration: underline; }
.offer-exit .offer { width: min(460px, 100%); padding: 0 0 18px; overflow: hidden; }
.offer-head { text-align: center; color: var(--offer-ink); padding: 22px 18px 18px; background: var(--offer); }
.offer-head h2 { color: var(--offer-ink); }
.offer-head p { margin: 0; opacity: .92; }
.offer-exit .offer-x { color: var(--offer-ink); }
.offer-logo { display: block; height: 26px; width: auto; margin: 2px auto 10px; }
.offer-box { display: grid; gap: 10px; margin: 16px 18px 4px; padding: 14px; border: 1px solid var(--offer-line); border-radius: 12px; background: var(--surface); }
.offer-box .offer-cta small { display: block; font-size: 12px; font-weight: 600; opacity: .8; margin-top: 2px; }
.offer-or { text-align: center; font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; }
.offer-cta2 { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 11px 13px; border: 1.5px solid var(--offer-line); border-radius: 10px; color: var(--text); text-decoration: none; font-weight: 700; }
.offer-cta2:hover { background: var(--offer-soft); color: #1f2937; }
.offer-cta2 s { color: var(--muted); font-weight: 600; }
.offer-cta2 .offer-clock { margin: 0; padding: 0; border: 0; background: none; overflow: visible; flex: none; }
.offer-cta2 .offer-clock i { display: none; }
.offer-box .offer-list { margin: 0; font-size: 14px; }
.offer-list { list-style: none; padding: 0; margin: 14px 18px; }
.offer-list li { position: relative; padding: 4px 0 4px 22px; }
.offer-cta { display: block; text-align: center; padding: 13px; border-radius: 10px; font-weight: 700; font-size: 17px; color: var(--offer-ink); text-decoration: none; background: var(--offer); border: 1px solid var(--offer-line); }
.offer-cta:hover { background: var(--offer-hover); }
@media (max-width: 560px) {
  .offer-plans { grid-template-columns: 1fr; }
  .offer h2 { font-size: 22px; }
  .offer-clock { flex-direction: column; align-items: flex-start; }
}

/* ---------- Cross-stitch chart maker ---------- */
.xs-preview { overflow: auto; max-height: 70vh; background: #fff; border: 1px solid var(--border); border-radius: var(--radius); }
.xs-canvas { display: block; max-width: none; }
.xs-legend-wrap { margin-top: 14px; overflow-x: auto; }
.xs-legend { border-collapse: collapse; width: 100%; font-size: 14px; }
.xs-legend th, .xs-legend td { border-bottom: 1px solid var(--border); padding: 6px 8px; text-align: left; }
.xs-sym { font-size: 18px; text-align: center; }
.xs-swatch { display: inline-block; width: 18px; height: 18px; border-radius: 4px; border: 1px solid rgba(0,0,0,.2); vertical-align: middle; }
#xsWideRow.is-off { opacity: .6; }
.xs-credit { margin-top: 10px; }

/* ---------- Time zone converter ---------- */
.tz-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)) minmax(0, 1.5fr) auto minmax(0, 1.5fr); gap: 10px; align-items: end; }
.tz-grid label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--muted); }
.tz-grid input, .tz-grid select { font: inherit; padding: 8px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--text); }
.tz-big { font-size: 24px; margin: 18px 0 4px; }
.tz-big span { font-size: 15px; color: var(--muted); margin-left: 6px; }
.tz-table { width: 100%; border-collapse: collapse; margin-top: 12px; font-size: 14px; }
.tz-table th, .tz-table td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--border); font-weight: 400; }
.tz-table tr.is-main { background: var(--accent-soft); font-weight: 600; }
@media (max-width: 760px) { .tz-grid { grid-template-columns: 1fr 1fr; } .tz-grid #tzSwap { grid-column: span 2; } }

/* Colours of a finished trace, under the download (paletteedit.js). */
.pal { margin: 12px 0 4px; text-align: left; }
.pal-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.pal-chip { display: inline-flex; align-items: center; gap: 6px; padding: 3px 4px 3px 3px; border: 1px solid var(--border); border-radius: 999px; background: var(--surface); font: 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace; }
.pal-sw { position: relative; width: 22px; height: 22px; border-radius: 50%; box-shadow: inset 0 0 0 1px rgba(0,0,0,.18); cursor: pointer; overflow: hidden; }
.pal-sw input { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%; border: 0; padding: 0; }
.pal-hex { color: var(--muted); }
.pal-x { border: 0; background: transparent; color: var(--muted); font-size: 16px; line-height: 1; width: 20px; height: 20px; border-radius: 50%; cursor: pointer; }
.pal-x:hover:not(:disabled) { background: var(--danger-soft); color: var(--danger); }
.pal-x:disabled { opacity: .3; cursor: default; }
.pal-stats { display: flex; flex-wrap: wrap; gap: 4px 14px; margin: 8px 0 2px; font-size: 12px; color: var(--muted); }
.pal-stats b { color: var(--text); font-variant-numeric: tabular-nums; }
.pal-hint { margin: 2px 0 0; font-size: 12px; }
.linkish { border: 0; background: none; padding: 0; color: var(--accent); text-decoration: underline; cursor: pointer; font: inherit; }

.fine-tune { margin-top: 6px; border-top: 1px solid var(--border); padding-top: 8px; }
.fine-tune > summary { cursor: pointer; font-size: 13px; font-weight: 600; color: var(--muted); margin-bottom: 8px; }
.fine-tune select { width: 100%; padding: 5px 7px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); color: var(--text); }

/* Zoom buttons over a CAD / toolpath preview (app.js zoomable). */
.zoombar { position: absolute; top: 8px; right: 8px; display: flex; align-items: center; gap: 4px; z-index: 3; }
.zoombar button { min-width: 30px; height: 30px; padding: 0 8px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--text); font: 600 14px/1 system-ui, sans-serif; cursor: pointer; box-shadow: 0 1px 3px rgba(0,0,0,.08); }
.zoombar button:hover { border-color: var(--accent); }
.zoombar-k { font: 12px/1 ui-monospace, monospace; color: var(--muted); padding: 0 4px; }

/* Lottie converter */
.lt-view { border-radius: 10px; overflow: hidden; margin: 12px 0; min-height: 40px; position: relative; }
.lt-view.lt-checker { background-color: #fff; background-image: linear-gradient(45deg, #e6e8ec 25%, transparent 25%, transparent 75%, #e6e8ec 75%), linear-gradient(45deg, #e6e8ec 25%, transparent 25%, transparent 75%, #e6e8ec 75%); background-size: 16px 16px; background-position: 0 0, 8px 8px; }
.lt-stage { width: 100%; max-height: 520px; margin: 0 auto; }
.lt-stage svg { display: block; }
.lt-player { display: flex; align-items: center; gap: 10px; margin: -4px 0 10px; }
.lt-player input[type=range] { flex: 1; }
.lt-at { font-variant-numeric: tabular-nums; font-size: 13px; color: var(--muted, #667); min-width: 74px; text-align: right; }
