/* ==========================================================================
   Illustrated Pet — "warm artisan print studio"
   Paper canvas, sienna ink, a serif display over a quiet sans. Every artwork
   sits in a mat-and-hairline frame; that frame is the signature device and is
   reused for the reveal, the gallery and the card preview.

   Self-hosted fonts only: the CSP is default-src 'self', so Google Fonts and
   any CDN are blocked. --font-display falls back through system serifs until
   a woff2 is added to web/fonts/ and the @font-face below is uncommented.
   ========================================================================== */

:root {
  /* ---- colour roles: one warm neutral ramp, one accent ------------------ */
  --paper:       #FAF6F0;   /* canvas */
  --paper-sunk:  #F2EBE1;   /* recessed band */
  --surface:     #FFFCF8;   /* raised card / mat */
  --ink:         #201B15;   /* body + headings (warm near-black, not #000) */
  --ink-soft:    #665C50;   /* secondary text — AA on paper */
  --rule:        #E4DACB;   /* hairlines */
  --rule-firm:   #CFC2AE;   /* emphasised hairlines */
  --accent:      #A24B24;   /* sienna — actions and emphasis only */
  --accent-deep: #83391A;   /* hover/active */
  --accent-wash: #F6EAE1;   /* accent at 8% for tints */
  --danger:      #A32A1F;

  /* ---- type ------------------------------------------------------------- */
  --font-display: "Newsreader", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  --font-body: ui-sans-serif, system-ui, sans-serif;

  /* 1.25 modular scale */
  --t-xs:   0.75rem;
  --t-sm:   0.8125rem;
  --t-base: 1.0625rem;
  --t-md:   1.25rem;
  --t-lg:   1.5rem;
  --t-xl:   1.875rem;
  --t-2xl:  2.375rem;
  --t-display: clamp(2.375rem, 5.5vw, 3.5rem);

  --measure: 66ch;

  /* ---- space: 4px base --------------------------------------------------- */
  --s-1: 0.25rem; --s-2: 0.5rem;  --s-3: 0.75rem; --s-4: 1rem;
  --s-5: 1.5rem;  --s-6: 2rem;    --s-7: 3rem;    --s-8: 4rem;   --s-9: 6rem;

  /* ---- radius, depth, motion --------------------------------------------- */
  --r-xs: 2px; --r-sm: 4px; --r-md: 8px; --r-lg: 14px;
  --shadow-1: 0 1px 2px rgba(32,27,21,.04), 0 2px 6px rgba(32,27,21,.05);
  --shadow-2: 0 2px 4px rgba(32,27,21,.05), 0 14px 34px rgba(32,27,21,.10);
  --dur-1: 160ms; --dur-2: 300ms;
  --ease: cubic-bezier(.2,.7,.3,1);
}

/* Self-hosted so it survives the CSP (default-src 'self' blocks Google Fonts and every CDN).
   Latin subset of the Newsreader variable face, SIL Open Font License, 129KB.
   unicode-range keeps the browser from reaching for this file to render glyphs it lacks. */
@font-face {
  font-family: "Newsreader";
  src: url("/fonts/newsreader-variable.woff2") format("woff2");
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                 U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
                 U+2212, U+2215, U+FEFF, U+FFFD;
}

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--t-base);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { font-family: var(--font-display); font-weight: 600; line-height: 1.12; letter-spacing: -0.015em; margin: 0; }
p { margin: 0; }
img { max-width: 100%; height: auto; }

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

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--r-xs); }

.wrap { width: 100%; max-width: 1120px; margin-inline: auto; padding-inline: var(--s-5); }
.muted { color: var(--ink-soft); font-size: var(--t-sm); }
.eyebrow {
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.14em;
  color: var(--ink-soft); font-weight: 600;
}

/* ---- masthead ------------------------------------------------------------ */
.masthead {
  border-bottom: 1px solid var(--rule);
  background: color-mix(in srgb, var(--paper) 80%, transparent);
  backdrop-filter: saturate(1.2) blur(6px);
  position: sticky; top: 0; z-index: 20;
}
/* centre, not baseline: the wordmark carries the logo mark now, so the note beside it should
   line up with the lockup as a whole rather than with the text's baseline. */
.masthead .wrap { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); padding-block: var(--s-4); }
.wordmark {
  font-family: var(--font-display); font-size: var(--t-md); font-weight: 600; color: var(--ink);
  text-decoration: none; letter-spacing: -0.01em;
  display: inline-flex; align-items: center; gap: var(--s-3);
  /* The mark costs ~42px of the row, which on a narrow phone was enough to break the brand name
     across two lines. The name never wraps; the tagline beside it gives way instead. */
  white-space: nowrap; flex: 0 0 auto;
}
/* The mark is black line art on transparency, so it reads on the paper masthead as drawn.
   Height-driven: it is a wide lockup (aspect 1.247) and the height is what carries the weight.
   Sized well above the 20px wordmark on purpose (Sam, 2026-09-06: "make the mark bigger") — the
   mark leads and the serif name reads as its companion. 54px starts to make a sticky header feel
   heavy; this is the last step before that. */
.wordmark-mark { height: 46px; width: auto; display: block; }
@media (max-width: 719px) { .wordmark-mark { height: 36px; } }
.wordmark:hover { color: var(--accent); }
.masthead-note { font-size: var(--t-xs); color: var(--ink-soft); letter-spacing: 0.02em; }

/* ---- the signature device: a matted, hairline-framed print --------------- */
.frame {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  padding: var(--s-3);
  box-shadow: var(--shadow-1);
}
.frame img { display: block; width: 100%; height: auto; border-radius: var(--r-xs); background: var(--paper-sunk); }

/* ---- hero ---------------------------------------------------------------- */
.hero .wrap {
  display: grid; gap: var(--s-8);
  padding-block: var(--s-8) var(--s-7);
  grid-template-columns: 1fr;
}
@media (min-width: 900px) {
  .hero .wrap { grid-template-columns: 1.05fr 0.95fr; align-items: center; padding-block: var(--s-9) var(--s-8); }
}
.hero h1 { font-size: var(--t-display); margin-block: var(--s-3) var(--s-4); max-width: 15ch; }
.hero .lede { font-size: var(--t-md); color: var(--ink-soft); max-width: 46ch; line-height: 1.55; }
.hero-cta { display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: center; margin-top: var(--s-6); }
.hero-fine { font-size: var(--t-sm); color: var(--ink-soft); margin-top: var(--s-3); }

/* three prints, fanned — deliberate asymmetry, not decoration for its own sake */
.hero-gallery { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-3); align-items: start; }
.hero-gallery .frame { transition: transform var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease); }
.hero-gallery .frame:nth-child(1) { transform: rotate(-2.5deg); }
.hero-gallery .frame:nth-child(2) { transform: translateY(-14px); z-index: 2; }
.hero-gallery .frame:nth-child(3) { transform: rotate(2.5deg); }
.hero-gallery .frame:hover { transform: translateY(-20px) rotate(0deg); box-shadow: var(--shadow-2); z-index: 3; }

/* The UA's [hidden] rule loses to any author `display:` (buttons, .btn, labels…) — so an element
   toggled via the hidden attribute would still render. Make hidden mean hidden, everywhere. */
[hidden] { display: none !important; }

/* ---- buttons ------------------------------------------------------------- */
button, .btn {
  font: inherit; font-size: var(--t-sm); font-weight: 600; letter-spacing: 0.01em;
  border-radius: var(--r-sm); padding: 0.7rem 1.15rem; cursor: pointer;
  border: 1px solid transparent; min-height: 44px;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  text-decoration: none;
  transition: background-color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
              color var(--dur-1) var(--ease), transform var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);
}
button:active:not(:disabled), .btn:active { transform: translateY(1px); }

button.primary, .btn-primary { background: var(--accent); color: var(--surface); box-shadow: var(--shadow-1); }
button.primary:hover:not(:disabled), .btn-primary:hover { background: var(--accent-deep); color: var(--surface); box-shadow: var(--shadow-2); }
button.primary:disabled { background: var(--rule-firm); color: var(--surface); cursor: not-allowed; box-shadow: none; }

button.secondary, .btn-secondary {
  background: var(--surface); color: var(--accent); border-color: var(--rule-firm);
}
button.secondary:hover:not(:disabled), .btn-secondary:hover { border-color: var(--accent); background: var(--accent-wash); }
button.secondary:disabled { color: var(--ink-soft); border-color: var(--rule); background: transparent; cursor: not-allowed; }

.btn-lg { font-size: var(--t-base); padding: 0.9rem 1.6rem; }

/* the third, quietest tier — used for anything that is not the main path */
button:not(.primary):not(.secondary) {
  background: transparent; color: var(--ink-soft); border-color: var(--rule);
}
button:not(.primary):not(.secondary):hover { color: var(--ink); border-color: var(--rule-firm); }

/* ---- pricing ------------------------------------------------------------- */
.prices { background: var(--paper-sunk); border-block: 1px solid var(--rule); padding-block: var(--s-7); }
.prices-head { display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: baseline; justify-content: space-between; margin-bottom: var(--s-5); }
.prices h2 { font-size: var(--t-lg); }
.price-list { display: grid; gap: var(--s-4); grid-template-columns: 1fr; }
@media (min-width: 720px) { .price-list { grid-template-columns: repeat(3, 1fr); } }
.price {
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--r-md);
  padding: var(--s-5); display: flex; flex-direction: column; gap: var(--s-2);
}
.price.is-value { border-color: var(--accent); box-shadow: var(--shadow-1); }
.price-name { font-family: var(--font-display); font-size: var(--t-md); font-weight: 600; }
.price-fig { font-size: var(--t-xl); font-family: var(--font-display); letter-spacing: -0.02em; }
.price-unit { font-size: var(--t-sm); color: var(--ink-soft); }
.price-tag {
  align-self: flex-start; font-size: var(--t-xs); font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--accent); background: var(--accent-wash);
  border-radius: 999px; padding: 0.2rem 0.6rem;
}

/* ---- how it works -------------------------------------------------------- */
.how .wrap { padding-block: var(--s-7); }
.how-list { display: grid; gap: var(--s-5); grid-template-columns: 1fr; counter-reset: how; margin-top: var(--s-5); }
@media (min-width: 720px) { .how-list { grid-template-columns: repeat(3, 1fr); gap: var(--s-6); } }
.how-item { counter-increment: how; padding-top: var(--s-4); border-top: 2px solid var(--rule-firm); }
.how-item::before {
  content: counter(how); font-family: var(--font-display); font-size: var(--t-md);
  color: var(--accent); display: block; margin-bottom: var(--s-1);
}
.how-item h3 { font-size: var(--t-base); margin-bottom: var(--s-1); }
.how-item p { font-size: var(--t-sm); color: var(--ink-soft); }

/* ---- studio -------------------------------------------------------------- */
.studio { padding-block: var(--s-8) var(--s-9); }
.studio-head { max-width: var(--measure); margin-bottom: var(--s-7); }
.studio-head h2 { font-size: var(--t-2xl); margin-bottom: var(--s-3); }

.step { border-top: 1px solid var(--rule); padding-block: var(--s-7); }
.step:first-of-type { border-top: none; padding-top: var(--s-2); }
.step-head { display: flex; gap: var(--s-4); align-items: baseline; margin-bottom: var(--s-5); }
.step-n {
  font-family: var(--font-display); font-size: var(--t-sm); font-weight: 700;
  color: var(--accent); background: var(--accent-wash);
  width: 2rem; height: 2rem; flex: none; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
}
.step h2 { font-size: var(--t-xl); }
.step-sub { max-width: var(--measure); color: var(--ink-soft); font-size: var(--t-sm); margin-top: var(--s-2); }

/* ---- template chooser (markup class + JS-generated classes) -------------- */
/* Occasion moved from a section heading onto the tile itself when the chooser became one grid. */
.tpl-occasion {
  display: block; font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.12em;
  color: var(--ink-soft); padding: 0 var(--s-1) var(--s-1);
}
.template-choice.selected .tpl-occasion { color: var(--accent); }
/* Fixed tracks, not fractions: most occasions hold only 1-2 designs, so `1fr`
   columns would blow a single card up to the full width and make the chooser
   thousands of pixels tall. */
.template-grid, .templates-row {
  display: grid; gap: var(--s-4); justify-content: start;
  grid-template-columns: repeat(auto-fill, minmax(132px, 150px));
}
@media (min-width: 640px) { .template-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 172px)); } }

.template-choice {
  border: 1px solid var(--rule); background: var(--surface); border-radius: var(--r-md);
  padding: var(--s-2); cursor: pointer; text-align: left; display: block; width: 100%;
  box-shadow: var(--shadow-1);
  transition: transform var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);
}
.template-choice img {
  width: 100%; aspect-ratio: 3/4; object-fit: cover; display: block; /* U5.2: mockup scene is 3:4 */
  border-radius: var(--r-xs); background: var(--paper-sunk);
}
.template-choice span {
  display: block; font-size: var(--t-sm); color: var(--ink); padding: var(--s-3) var(--s-1) 0;
}
.template-choice:hover { transform: translateY(-3px); border-color: var(--rule-firm); box-shadow: var(--shadow-2); }
.template-choice.selected { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent), var(--shadow-2); }
.template-choice.selected span { color: var(--accent); font-weight: 600; }

/* ---- chooser filters (U4.1: browse by theme, filter by occasion) --------- */
.chooser-filters { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); margin-bottom: var(--s-5); }
.filter-chips { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.filter-chip {
  font: inherit; font-size: var(--t-sm); line-height: 1;
  border: 1px solid var(--rule-firm); background: var(--surface); color: var(--ink-soft);
  border-radius: 999px; padding: 0.5rem 0.9rem; cursor: pointer;
  transition: color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease), background-color var(--dur-1) var(--ease);
}
.filter-chip:hover { color: var(--ink); border-color: var(--accent); }
.filter-chip.is-active { background: var(--accent-wash); border-color: var(--accent); color: var(--accent); font-weight: 600; }
.filter-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.filter-occasion {
  font: inherit; font-size: var(--t-sm); color: var(--ink);
  border: 1px solid var(--rule-firm); background: var(--surface); border-radius: var(--r-sm);
  padding: 0.5rem 0.6rem; cursor: pointer;
}
.filter-occasion:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.filter-count { font-size: var(--t-sm); color: var(--ink-soft); margin-left: auto; white-space: nowrap; }
.chooser-empty { color: var(--ink-soft); font-size: var(--t-sm); padding: var(--s-5) 0; }
@media (max-width: 480px) { .filter-count { margin-left: 0; width: 100%; } }

/* ---- U4.4: two-pet badge + upload slots ---------------------------------- */
.template-choice { position: relative; }
.tpl-badge {
  position: absolute; top: var(--s-3); left: var(--s-3);
  background: var(--accent); color: #fff; font-size: var(--t-xs); font-weight: 600;
  padding: 0.15rem 0.5rem; border-radius: 999px; letter-spacing: 0.02em;
}
.upload-slots { display: flex; flex-wrap: wrap; gap: var(--s-5); align-items: flex-start; }
.upload-slot-label { display: block; font-size: var(--t-sm); font-weight: 600; margin-bottom: var(--s-2); color: var(--ink); }

/* ---- the ad band: the product in use ------------------------------------- */
/* U6.2. Copy sits UNDER each image rather than over it: the site's whole register is restrained,
   and overlaid type on a warm photographed interior is a legibility gamble the brand does not need.
   The images are 16:9 and carry width/height so the page cannot reflow as they load. */
.scene-band {
  border-top: 1px solid var(--rule); padding-block: var(--s-7);
  display: grid; gap: var(--s-7); grid-template-columns: 1fr;
}
@media (min-width: 900px) { .scene-band { grid-template-columns: 1fr 1fr; gap: var(--s-6); } }
.scene { margin: 0; }
/* No aspect-ratio and no object-fit: each image element carries its real width and height, so
   the browser reserves the exact box and the whole render shows uncropped. */
.scene img {
  display: block; width: 100%; height: auto;
  border-radius: var(--r-md); background: var(--paper-sunk); box-shadow: var(--shadow-1);
}
.scene figcaption { margin-top: var(--s-4); }
.scene figcaption h2 { font-size: var(--t-md); margin-bottom: var(--s-2); }
.scene figcaption p { color: var(--ink-soft); font-size: var(--t-sm); max-width: 44ch; }

/* ---- upload: the dropzone IS the call to action -------------------------- */
/* U6.1: was a bare <input type=file> behind a step-number badge. The upload is the only thing
   this page asks for, so it gets the weight: one large target, click OR drag, with rotating
   photo guidance inside it. The <label for> makes the whole panel clickable and keeps the real
   input as the accessible control — .sr-only hides it visually but leaves it focusable, and
   :focus-within paints the ring on the panel. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
.dropzone {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s-2); text-align: center; cursor: pointer;
  min-height: 210px; padding: var(--s-6) var(--s-5);
  border: 1px dashed var(--rule-firm); border-radius: var(--r-md);
  background: var(--surface); color: var(--ink);
  transition: border-color var(--dur-1) var(--ease), background-color var(--dur-1) var(--ease),
              box-shadow var(--dur-1) var(--ease);
}
.dropzone:hover { border-color: var(--accent); background: var(--accent-wash); }
.upload-slot:focus-within .dropzone { outline: 2px solid var(--accent); outline-offset: 3px; }
.dropzone.is-dragging { border-color: var(--accent); border-style: solid; background: var(--accent-wash); box-shadow: var(--shadow-2); }
.dropzone.has-file { border-style: solid; border-color: var(--rule-firm); background: var(--paper-sunk); }
.dropzone-icon { width: 30px; height: 30px; color: var(--accent); }
.dropzone-action { font-family: var(--font-display); font-size: var(--t-md); font-weight: 600; }
.dropzone-sub { font-size: var(--t-sm); color: var(--ink-soft); }
/* The rotating hint. Reserved height so the panel never jumps as the text swaps. */
.dropzone-tip {
  margin-top: var(--s-3); min-height: 2.6em; max-width: 30ch;
  font-size: var(--t-sm); color: var(--ink-soft);
  display: flex; align-items: center; justify-content: center;
  transition: opacity var(--dur-1) var(--ease);
}
.dropzone-tip.is-swapping { opacity: 0; }
.dropzone-chosen { font-size: var(--t-sm); font-weight: 600; color: var(--accent); word-break: break-all; }
.upload-fineprint { margin-top: var(--s-3); }
@media (max-width: 719px) { .dropzone { min-height: 170px; padding: var(--s-5) var(--s-4); } }
/* Rotating text is motion. When it is not wanted, the tips stop cycling — upload.js renders them
   as one static line instead, so none of the guidance is lost. */
@media (prefers-reduced-motion: reduce) { .dropzone-tip { transition: none; min-height: 0; } }

/* ---- upload -------------------------------------------------------------- */
input[type=file] { font: inherit; font-size: var(--t-sm); color: var(--ink-soft); max-width: 100%; }
input[type=file]::file-selector-button {
  font: inherit; font-size: var(--t-sm); font-weight: 600;
  background: var(--surface); color: var(--accent); border: 1px solid var(--rule-firm);
  border-radius: var(--r-sm); padding: 0.6rem 1rem; margin-right: var(--s-3); cursor: pointer;
  transition: background-color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease);
}
input[type=file]::file-selector-button:hover { background: var(--accent-wash); border-color: var(--accent); }

/* ---- status -------------------------------------------------------------- */
.status { font-size: var(--t-sm); color: var(--ink-soft); margin-top: var(--s-3); min-height: 1.4em; }
.status.error { color: var(--danger); font-weight: 600; }

/* ---- the reveal: the one moment the whole product turns on ---------------
   U5.5: this is the payoff, so it gets the screen — centred, no competing chrome, and no mat
   frame around it. The image is already a photographed product shot on its own studio ground;
   framing a photo of a card reads as fussy and shrinks the card twice over. */
.reveal-hero { text-align: center; padding: var(--s-6) 0 var(--s-7); }
.reveal-eyebrow {
  font-size: var(--t-xs); font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-soft); margin-bottom: var(--s-3);
}
.reveal-title { font-size: var(--t-2xl); margin-bottom: var(--s-5); }
.reveal-note {
  margin: var(--s-5) auto 0; max-width: 46ch; color: var(--ink-soft); font-size: var(--t-sm);
}
#reveal-wrap { display: flex; justify-content: center; }
#reveal-img {
  width: 100%; max-width: 480px; display: block; border-radius: var(--r-sm);
  background: var(--paper-sunk); aspect-ratio: 3/4; object-fit: cover;
  animation: reveal var(--dur-2) var(--ease) both;
}
@keyframes reveal { from { opacity: 0; transform: scale(.985) translateY(6px); } to { opacity: 1; transform: none; } }

/* ---- change-it options: one quiet line directly under the card ----------- */
/* U6.5 (Sam: put it where the note was, "and make it smaller"). It replaced a full section with a
   heading; now it is a single centred row that does not compete with the reveal above it. */
.refine-row {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: var(--s-3); margin-top: var(--s-5);
}
.refine-label { font-size: var(--t-sm); color: var(--ink-soft); }
.btn-sm { font-size: var(--t-xs); padding: 0.4rem 0.75rem; min-height: 34px; }

/* ---- fix a spot: the drag happens ON the revealed card -------------------- */
/* U6.5 (Sam: "have the fix a spot tool work on the card image ... rather than popping up another
   image"). The reveal is a product shot, so the drag is clamped to the printed panel — a box over
   the table or the fold would map nowhere. Coordinates leave as PANEL fractions; the server turns
   them into master pixels (it alone knows the master size and the cover-fit crop). */
#reveal-stage { position: relative; display: inline-block; line-height: 0; }
#reveal-stage.is-fixing { cursor: crosshair; }
#reveal-stage.is-fixing::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  box-shadow: 0 0 0 2px var(--accent); border-radius: var(--r-sm);
}
#reveal-stage #fix-rect {
  position: absolute; display: none; pointer-events: none;
  border: 2px solid var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.fix-hint { margin-top: var(--s-4); }
.fix-row { display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: center; justify-content: center; margin-top: var(--s-3); }
.fix-row input[type=text] { flex: 1 1 22rem; max-width: 34rem; }
#fix-section { text-align: center; }
#fix-section .fix-result-row { justify-content: center; }


/* ---- fix editor ---------------------------------------------------------- */
#fix-canvas-wrap { position: relative; display: inline-block; max-width: 100%; cursor: crosshair; border-radius: var(--r-sm); overflow: hidden; }
#fix-target-img { display: block; max-width: 100%; height: auto; user-select: none; -webkit-user-drag: none; }
#fix-rect { position: absolute; border: 2px dashed var(--accent); background: rgba(162,75,36,.14); display: none; pointer-events: none; }
#fix-note {
  width: 100%; max-width: 480px; margin-top: var(--s-3); padding: 0.6rem 0.75rem;
  font: inherit; font-size: var(--t-sm);
}
.fix-actions { margin-top: var(--s-4); display: flex; gap: var(--s-3); flex-wrap: wrap; }
.fix-result-row { display: flex; gap: var(--s-5); margin-top: var(--s-5); flex-wrap: wrap; }
.fix-result-row figure { margin: 0; }
.fix-result-row img { max-width: 260px; display: block; border-radius: var(--r-sm); border: 1px solid var(--rule); }
.fix-result-row figcaption { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.12em; color: var(--ink-soft); margin-top: var(--s-2); }

/* ---- card form ----------------------------------------------------------- */
.card-layout { display: grid; gap: var(--s-7); grid-template-columns: 1fr; align-items: start; }
@media (min-width: 860px) { .card-layout { grid-template-columns: 1fr auto; } }
.card-form label { display: block; font-size: var(--t-sm); font-weight: 600; color: var(--ink); margin-top: var(--s-4); }
.card-form label:first-child { margin-top: 0; }
.card-form input[type=text], .card-form textarea, .card-form select, #fix-note {
  width: 100%; max-width: 460px; margin-top: var(--s-2); padding: 0.6rem 0.75rem;
  font: inherit; font-size: var(--t-sm); font-weight: 400; color: var(--ink);
  background: var(--surface); border: 1px solid var(--rule-firm); border-radius: var(--r-sm);
  transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);
}
.card-form input[type=text]:hover, .card-form textarea:hover, .card-form select:hover { border-color: var(--ink-soft); }
.card-form input[type=text]:focus, .card-form textarea:focus, .card-form select:focus, #fix-note:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-wash);
}
.card-form textarea { resize: vertical; }
.card-form-row { display: flex; gap: var(--s-4); flex-wrap: wrap; }
.card-form-row label { flex: 1 1 160px; }

#card-preview-wrap { margin-top: 0; }
/* U5.1d: 3:4, matching the card mockup the preview now shows (was 250x350, the bare 5:7 panel). */
#card-preview {
  position: relative; width: 250px; height: 333px; overflow: hidden;
  border-radius: var(--r-sm); background: var(--paper-sunk);
}
#card-preview img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Positioned over the card's front panel by updateCardPreview() from /api/mockup — the offsets
   here are only the fallback for the moment before that lands. */
#card-preview-text {
  position: absolute; left: 0; top: 0; width: 100%; height: 100%;
  display: flex; align-items: flex-end; justify-content: center; text-align: center;
  color: #fff; text-shadow: 1px 1px 3px rgba(0,0,0,.65);
  padding: 0 8% 9% 8%; word-wrap: break-word; overflow-wrap: anywhere; pointer-events: none;
}

/* ---- buy ----------------------------------------------------------------- */
.buy-panel {
  margin-top: var(--s-7); background: var(--surface); border: 1px solid var(--rule-firm);
  border-radius: var(--r-md); padding: var(--s-5); box-shadow: var(--shadow-1);
}
/* U5.5: the panel now follows the reveal hero, which already carries its own bottom padding. */
#buy-section .buy-panel { margin-top: 0; }
/* h2, not h3: U5.5 lifted this panel to sit directly under the reveal's h1, so an h3 here would
   skip a heading level and break the document outline for screen readers. */
.buy-panel h2 { font-size: var(--t-md); margin-bottom: var(--s-2); }
.buy-row { display: flex; gap: var(--s-4); flex-wrap: wrap; align-items: flex-end; margin-top: var(--s-4); }
.buy-row label { display: block; font-size: var(--t-sm); font-weight: 600; flex: 1 1 280px; }
.buy-note { font-size: var(--t-xs); color: var(--ink-soft); margin-top: var(--s-4); }

/* ---- pack chooser + sticky buy bar --------------------------------------- */
.packs { border: 0; margin: 0; padding: 0; }
.packs legend { padding: 0; margin-bottom: var(--s-3); }
.pack-list { display: grid; gap: var(--s-3); }
@media (min-width: 720px) { .pack-list { grid-template-columns: repeat(3, 1fr); align-items: stretch; } }

.pack { position: relative; display: block; cursor: pointer; }
/* The input stays focusable and reachable by keyboard — clipped, never display:none. */
.pack input {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.pack-body {
  display: flex; flex-direction: column; gap: var(--s-1); height: 100%;
  border: 1px solid var(--rule-firm); border-radius: var(--r-md);
  padding: var(--s-4); background: var(--paper);
  transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease),
              background-color var(--dur-1) var(--ease), transform var(--dur-1) var(--ease);
}
.pack:hover .pack-body { border-color: var(--accent); transform: translateY(-2px); }
.pack input:checked + .pack-body {
  border-color: var(--accent); background: var(--surface);
  box-shadow: 0 0 0 1px var(--accent), var(--shadow-1);
}
.pack input:focus-visible + .pack-body { outline: 2px solid var(--accent); outline-offset: 3px; }
.pack-name { font-weight: 600; font-size: var(--t-sm); }
.pack-price { font-family: var(--font-display); font-size: var(--t-lg); letter-spacing: -0.02em; }
.pack-note { font-size: var(--t-xs); color: var(--ink-soft); }
.pack-tag {
  align-self: flex-start; font-size: var(--t-xs); font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--accent); background: var(--accent-wash);
  border-radius: 999px; padding: 0.15rem 0.55rem; margin-bottom: var(--s-1);
}

/* U5.5b: fixed at EVERY width, not just phones. Moving the order panel above the message form
   meant a desktop customer who scrolled down to add a message had to scroll back up to Buy — the
   bar removes that entirely, and keeps "now buy" one click away from any point on the page.
   Full-bleed background, but the CONTENTS align to the same 1120px column as .wrap, so on a wide
   screen the price and button sit under the page's own grid rather than pinned to the far edges. */
.buy-bar {
  display: flex; align-items: center; gap: var(--s-4);
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  margin: 0;
  padding-block: var(--s-3);
  padding-bottom: calc(var(--s-3) + env(safe-area-inset-bottom, 0px));
  padding-inline: max(var(--s-4), calc((100vw - 1120px) / 2 + var(--s-5)));
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  backdrop-filter: saturate(1.3) blur(8px);
  border-top: 1px solid var(--rule-firm);
  box-shadow: 0 -6px 24px rgba(32,27,21,.10);
}
.buy-bar-sum { display: flex; align-items: baseline; gap: var(--s-3); flex: 1 1 auto; }
.buy-bar-label { font-size: var(--t-sm); color: var(--ink-soft); }
.buy-bar-price { font-family: var(--font-display); font-size: var(--t-md); }
.buy-bar #buy-btn { flex: 0 0 auto; min-width: 8.5rem; }

/* ---- studio tools (fulfillment-only, kept out of the customer's way) ----- */
.studio-tools { margin-top: var(--s-6); border-top: 1px dashed var(--rule); padding-top: var(--s-4); }
.studio-tools summary { font-size: var(--t-xs); color: var(--ink-soft); cursor: pointer; letter-spacing: 0.06em; text-transform: uppercase; }
.studio-tools[open] summary { margin-bottom: var(--s-3); }

/* ---- footer -------------------------------------------------------------- */
footer.site {
  border-top: 1px solid var(--rule); background: var(--paper-sunk);
  padding-block: var(--s-6); font-size: var(--t-sm); color: var(--ink-soft);
}
footer.site .wrap { display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-5); justify-content: space-between; align-items: center; }
footer.site nav { display: flex; gap: var(--s-5); flex-wrap: wrap; }

/* ---- motion preference --------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
  .hero-gallery .frame, .hero-gallery .frame:nth-child(1), .hero-gallery .frame:nth-child(2), .hero-gallery .frame:nth-child(3) { transform: none; }
}
/* Must come after the footer.site rule above: same specificity, so source order decides.
   Without this the fixed buy bar covers the last ~84px of the footer. The bar is fixed at every
   width now (U5.5b), so the clearance is no longer phone-only — but it is scoped to pages that
   actually HAVE a bar, or the gallery and style pages would carry 5.5rem of dead space for
   nothing. .buy-bar exists only in create.html; where :has() is unsupported the bar simply
   overlaps the footer, which is what it did before. */
body:has(.buy-bar) footer.site { padding-bottom: calc(var(--s-6) + 5.5rem); }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* ---- U5.3: style page + link tiles ----------------------------------------- */
a.template-choice { text-decoration: none; color: inherit; }
.style-hero { display: grid; gap: var(--s-6); grid-template-columns: 1fr; align-items: start; margin-bottom: var(--s-7); }
@media (min-width: 760px) { .style-hero { grid-template-columns: minmax(280px, 420px) 1fr; gap: var(--s-8); } }
.style-hero-card img { width: 100%; height: auto; display: block; border-radius: var(--r-md); box-shadow: var(--shadow-2); }
.style-hero h1 { font-size: var(--t-2xl); margin: var(--s-2) 0 var(--s-3); }
/* U6.4: the upload sits in the hero's right column, directly under the card title — Sam wanted the
   tool beside the sample card rather than a scroll below it, and the explainer paragraph gone. It
   is no longer a page-level .step, so it carries none of that chrome. */
.hero-upload { margin-top: var(--s-5); }
.hero-upload h2 { font-size: var(--t-md); margin-bottom: var(--s-4); }
/* In a hero column the zone has less room than it did full-width, so it gets shorter rather than
   pushing the checkmarks off the fold. */
.hero-upload .dropzone { min-height: 168px; padding: var(--s-5) var(--s-4); }
.hero-upload .upload-fineprint { margin-top: var(--s-3); }
.style-trust { list-style: none; padding: 0; margin: var(--s-6) 0 0; display: grid; gap: var(--s-2); color: var(--ink-soft); font-size: var(--t-sm); }
.style-trust li { padding-left: 1.4em; position: relative; }
.style-trust li::before { content: "\2713"; position: absolute; left: 0; color: var(--accent); font-weight: 700; }
.masthead a.masthead-note { text-decoration: none; }

/* ---- U5.4: the creation page (magic wait) ------------------------------------- */
.magic { text-align: center; max-width: 34rem; margin: var(--s-6) auto var(--s-8); }
.magic-card-wrap { width: min(280px, 70vw); margin: 0 auto var(--s-6); }
.magic-card { width: 100%; height: auto; display: block; border-radius: var(--r-md); box-shadow: var(--shadow-2);
  animation: magic-breathe 2.4s ease-in-out infinite; }
@keyframes magic-breathe { 0%, 100% { opacity: 0.55; transform: scale(0.985); } 50% { opacity: 1; transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .magic-card { animation: none; opacity: 0.85; } }
.magic-title { font-size: var(--t-xl); margin-bottom: var(--s-3); }
.magic-status { min-height: 1.6em; }
.magic-note { margin-top: var(--s-4); font-size: var(--t-sm); }
#magic-back { margin-top: var(--s-5); display: inline-block; }
