/* PageMasters estimate calculator
 *
 * Styling follows pagemasters.co: Wolpe Pegasus, cream ground, black ink,
 * heavy black rules and a single warm accent. Font files are copied from the
 * live site and sit at the same /assets/fonts path, so this works locally and
 * still resolves once the tool is ported into Craft.
 */

@font-face {
  font-family: "Wolpe Pegasus";
  src: url("../assets/fonts/WolpePegasus-Regular.woff2") format("woff2"),
       url("../assets/fonts/WolpePegasus-Regular.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Wolpe Pegasus";
  src: url("../assets/fonts/WolpePegasus-Bold.woff2") format("woff2"),
       url("../assets/fonts/WolpePegasus-Bold.woff") format("woff");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Wolpe Pegasus";
  src: url("../assets/fonts/WolpePegasus-Italic.woff2") format("woff2"),
       url("../assets/fonts/WolpePegasus-Italic.woff") format("woff");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: "Wolpe Pegasus";
  src: url("../assets/fonts/WolpePegasus-BoldItalic.woff2") format("woff2"),
       url("../assets/fonts/WolpePegasus-BoldItalic.woff") format("woff");
  font-weight: 700;
  font-style: italic;
  font-display: swap;
}

:root {
  /* Lifted from pagemasters.co */
  --paper: #fffaf0;          /* body background */
  --ink: #000;
  --accent: #e85c41;         /* the one warm accent on the site */
  --hot: #ff00ff;            /* the magenta from the icons - hover and focus */

  --muted: #6b655c;          /* secondary text, warmed to sit on cream */
  --line: #cfc7b8;           /* light rules, warmed */
  --tint: rgba(0, 0, 0, 0.04);
  --field: #fff;             /* inputs are white on the live site */


  /* Decorative section border - a woven rope, drawn as a border-image.
   *
   * 100x100 with a 20 slice: four corner bosses and 60-long edge strips
   * carrying one period of two sine strands a half-period apart. They cross at
   * each strip end and at its midpoint, and the strip leaves and arrives at
   * the same height and heading, so tiles join invisibly.
   *
   * The over-and-under is done with casing, not path splitting: each strand is
   * a wide stroke in the background colour followed by a thin one in ink, and
   * painting the second strand's casing after the first strand's ink is what
   * cuts the one underneath. That means the casing colour has to match
   * whatever the box is sitting on, which is the only reason there are two
   * variants - both draw the same black rope, one cased for paper and one for
   * the tinted ground of a completed section.
   *
   * Generated by tools/rope-border.py - edit there, not here. */
  --rope-ink: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Cg fill='none' stroke='%23fffaf0' stroke-width='9.52' stroke-linecap='butt'%3E%3Cpath d='M20,10 C30,0 40,0 50,10 C60,20 70,20 80,10'/%3E%3Cpath d='M20,90 C30,100 40,100 50,90 C60,80 70,80 80,90'/%3E%3Cpath d='M10,20 C0,30 0,40 10,50 C20,60 20,70 10,80'/%3E%3Cpath d='M90,20 C100,30 100,40 90,50 C80,60 80,70 90,80'/%3E%3C/g%3E%3Cg fill='none' stroke='%23000' stroke-width='2.8' stroke-linecap='butt'%3E%3Cpath d='M20,10 C30,0 40,0 50,10 C60,20 70,20 80,10'/%3E%3Cpath d='M20,90 C30,100 40,100 50,90 C60,80 70,80 80,90'/%3E%3Cpath d='M10,20 C0,30 0,40 10,50 C20,60 20,70 10,80'/%3E%3Cpath d='M90,20 C100,30 100,40 90,50 C80,60 80,70 90,80'/%3E%3C/g%3E%3Cg fill='none' stroke='%23fffaf0' stroke-width='9.52' stroke-linecap='butt'%3E%3Cpath d='M20,10 C30,20 40,20 50,10 C60,0 70,0 80,10'/%3E%3Cpath d='M20,90 C30,80 40,80 50,90 C60,100 70,100 80,90'/%3E%3Cpath d='M10,20 C20,30 20,40 10,50 C0,60 0,70 10,80'/%3E%3Cpath d='M90,20 C80,30 80,40 90,50 C100,60 100,70 90,80'/%3E%3C/g%3E%3Cg fill='none' stroke='%23000' stroke-width='2.8' stroke-linecap='butt'%3E%3Cpath d='M20,10 C30,20 40,20 50,10 C60,0 70,0 80,10'/%3E%3Cpath d='M20,90 C30,80 40,80 50,90 C60,100 70,100 80,90'/%3E%3Cpath d='M10,20 C20,30 20,40 10,50 C0,60 0,70 10,80'/%3E%3Cpath d='M90,20 C80,30 80,40 90,50 C100,60 100,70 90,80'/%3E%3C/g%3E%3Cg fill='%23000'%3E%3Cpath d='M10,2 L18,10 L10,18 L2,10 Z'/%3E%3Cpath d='M90,2 L98,10 L90,18 L82,10 Z'/%3E%3Cpath d='M10,82 L18,90 L10,98 L2,90 Z'/%3E%3Cpath d='M90,82 L98,90 L90,98 L82,90 Z'/%3E%3C/g%3E%3C/svg%3E");
  --rope-done: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Cg fill='none' stroke='%23f5f0e6' stroke-width='9.52' stroke-linecap='butt'%3E%3Cpath d='M20,10 C30,0 40,0 50,10 C60,20 70,20 80,10'/%3E%3Cpath d='M20,90 C30,100 40,100 50,90 C60,80 70,80 80,90'/%3E%3Cpath d='M10,20 C0,30 0,40 10,50 C20,60 20,70 10,80'/%3E%3Cpath d='M90,20 C100,30 100,40 90,50 C80,60 80,70 90,80'/%3E%3C/g%3E%3Cg fill='none' stroke='%23000' stroke-width='2.8' stroke-linecap='butt'%3E%3Cpath d='M20,10 C30,0 40,0 50,10 C60,20 70,20 80,10'/%3E%3Cpath d='M20,90 C30,100 40,100 50,90 C60,80 70,80 80,90'/%3E%3Cpath d='M10,20 C0,30 0,40 10,50 C20,60 20,70 10,80'/%3E%3Cpath d='M90,20 C100,30 100,40 90,50 C80,60 80,70 90,80'/%3E%3C/g%3E%3Cg fill='none' stroke='%23f5f0e6' stroke-width='9.52' stroke-linecap='butt'%3E%3Cpath d='M20,10 C30,20 40,20 50,10 C60,0 70,0 80,10'/%3E%3Cpath d='M20,90 C30,80 40,80 50,90 C60,100 70,100 80,90'/%3E%3Cpath d='M10,20 C20,30 20,40 10,50 C0,60 0,70 10,80'/%3E%3Cpath d='M90,20 C80,30 80,40 90,50 C100,60 100,70 90,80'/%3E%3C/g%3E%3Cg fill='none' stroke='%23000' stroke-width='2.8' stroke-linecap='butt'%3E%3Cpath d='M20,10 C30,20 40,20 50,10 C60,0 70,0 80,10'/%3E%3Cpath d='M20,90 C30,80 40,80 50,90 C60,100 70,100 80,90'/%3E%3Cpath d='M10,20 C20,30 20,40 10,50 C0,60 0,70 10,80'/%3E%3Cpath d='M90,20 C80,30 80,40 90,50 C100,60 100,70 90,80'/%3E%3C/g%3E%3Cg fill='%23000'%3E%3Cpath d='M10,2 L18,10 L10,18 L2,10 Z'/%3E%3Cpath d='M90,2 L98,10 L90,18 L82,10 Z'/%3E%3Cpath d='M10,82 L18,90 L10,98 L2,90 Z'/%3E%3Cpath d='M90,82 L98,90 L90,98 L82,90 Z'/%3E%3C/g%3E%3C/svg%3E");

  --rule-weight: 2px;        /* their signature border */
  --font: "Wolpe Pegasus", Georgia, "Times New Roman", serif;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

body {
  font-family: var(--font);
  background: var(--paper);
  color: var(--ink);
  max-width: 720px;
  margin: 20px auto;
  padding: 0 16px 120px;
  font-size: 18px;
  line-height: 1.1;
  /* Fill the window even when the form is short. The gem field is pinned to
     this box, so without it the landing page has no field below the form and
     the bottom of the screen sits bare. The 40px is the top and bottom margin,
     so the body still ends exactly at the fold and adds no scroll. */
  min-height: calc(100vh - 40px);
}

/* Masthead: lino-cut mark centred over the blackletter wordmark, both from
 * pagemasters.co. The width/height attributes on each <img> hold the space so
 * the header doesn't jump as they load. */
header {
  margin-bottom: 32px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 10px;
}

.logo-mark {
  display: block;
  width: 104px;
  height: auto;
  margin-bottom: 4px;
}

/* Full width - the h1 is a flex item in a centred column, so it needs to
   stretch before the image inside it can fill anything. */
header h1 { margin: 0; line-height: 0; align-self: stretch; }

.logo-word {
  display: block;
  width: 100%;
  height: auto;
}

header p { color: var(--muted); }
.header-note { font-size: 15px; color: var(--muted); max-width: 52ch; }

/* Keyboard focus. Same magenta as the icons, and never removed - a control
   you can reach with Tab has to show where you are. */
:focus-visible {
  outline: var(--rule-weight) solid var(--hot);
  outline-offset: 2px;
}

.colour-btn:focus-visible .radio,
.sides-btn:focus-visible .radio,
.bleed-btn:focus-visible .radio,
.orientation-btn:focus-visible .radio,
.binding-btn:focus-visible .radio,
.cover-btn:focus-visible .radio,
.delivery-btn:focus-visible .radio,
.paper-weight-btn:focus-visible .radio { background: var(--hot); }

/* Once a choice is made, the roads not taken step back a little.
 *
 * The dim is carried on a custom property set on the group, so the child rule
 * can stay generic - every pick-one container is named `*-grid` and holds
 * exactly one class, and `.field-grid` (the name and project inputs) never
 * contains a `.selected`, so it resolves to 1 and is left alone.
 *
 * Unpicked options come back to full strength on hover and on keyboard focus.
 * Without that this reads as "these are disabled" rather than "you picked that
 * one", and every option here stays changeable to the end. */
:where(.experience-grid, .product-grid, .size-grid, .sides-grid,
       .orientation-grid, .delivery-grid, .cover-grid, .binding-grid,
       .paper-family-grid, .paper-shade-grid, .paper-weight-grid,
       .colour-grid, .page-select-grid):has(> .selected) {
  --unpicked: 0.45;
}

[class$="-grid"] > :not(.selected) {
  opacity: var(--unpicked, 1);
  transition: opacity 0.15s ease;
}

[class$="-grid"] > :not(.selected):hover,
[class$="-grid"] > :not(.selected):focus-visible,
[class$="-grid"] > :not(.selected):focus-within {
  opacity: 1;
}

/* Panels - the estimate form and saved estimates swap places */
.tab-panel { display: none; }
.tab-panel.active { display: block; }

/* Modules */
.module {
  margin-bottom: 16px;
  transition: opacity 0.3s;
}

/* Utility - must beat later display rules like .running-total's flex */
.hidden { display: none !important; }

.module.completed .module-section {
  /* Same black rope, but its casing is mixed for the tinted ground below -
     the paper-cased one would show pale gaps through the weave. A completed
     step reads as done by its tint, not by a lighter border. */
  border-image: var(--rope-done) 20 round;
  /* --tint is translucent, so it needs an opaque ground under it rather than
     letting the gems ghost through a completed step */
  background-color: var(--paper);
  background-image: linear-gradient(var(--tint), var(--tint));
}

.module-section {
  border: 12px solid var(--ink);
  border-image: var(--rope-ink) 20 round;
  padding: 18px;
  /* Opaque - the gems pass behind these rather than through them, and the
     rope's casing is painted in this colour */
  background: var(--paper);
}

.module-section h2 {
  font-size: 25px;
  line-height: 1.1;
  font-weight: 700;
  margin-bottom: 6px;
}

/* The prompt under each heading. Plain italic - Wolpe Pegasus has a real
   italic cut, so this isn't a synthesised slant. */
.module-description {
  font-size: 16px;
  font-style: italic;
  color: var(--muted);
  margin-bottom: 20px;
  max-width: 58ch;
}

.module-next {
  margin-top: 16px;
}

/* Experience / artwork help - a stacked list, since the labels are sentences
   rather than single words */
.sub-heading {
  font-size: 18px;
  font-weight: 700;
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}

.experience-grid {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.experience-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  width: 100%;
  padding: 12px 4px;
  border: none;
  border-bottom: 1px solid var(--line);
  background: none;
  cursor: pointer;
  font: inherit;
  text-align: left;
  color: var(--ink);
}

.experience-btn:last-child { border-bottom: none; }

.experience-text { display: flex; flex-direction: column; gap: 2px; }
.experience-label { font-weight: 700; font-size: 16px; }
.experience-hint { font-size: 14px; color: var(--muted); }

.experience-btn:hover .radio { background: var(--hot); }
.experience-btn.selected .radio { background: var(--ink); }
.experience-btn.selected .experience-label { text-decoration: underline; }

/* Product type selector */
.product-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  align-items: stretch;
}

/* Same selection language as every other pick-one control: icon, label, then
   the round radio. Bigger padding and icon because this is the top-level
   choice, but nothing that reads as a different kind of control. */
.product-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 18px 12px;
  border: none;
  background: none;
  cursor: pointer;
  font: inherit;
  text-align: center;
  color: var(--ink);
}

.product-card:hover .radio { background: var(--hot); }
.product-card.selected .radio { background: var(--ink); }
.product-card.selected .product-label { text-decoration: underline; }

/* Size selector */
.size-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: 10px;
}

/* No border here either - the size icon is itself a rectangle, so a box round
   it read as a rectangle inside a rectangle. */
.size-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 14px 8px;
  border: none;
  background: none;
  cursor: pointer;
  font: inherit;
  text-align: center;
  color: var(--ink);
}

.size-btn:hover .radio { background: var(--hot); }
.size-btn.selected .radio { background: var(--ink); }
.size-btn.selected .size-label { text-decoration: underline; }


.size-label {
  font-weight: 700;
  font-size: 15px;
}

.size-hint {
  font-size: 11px;
  color: var(--muted);
}

/* Sides selector */
.sides-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.sides-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 12px 8px;
  border: none;
  background: none;
  cursor: pointer;
  font: inherit;
  text-align: center;
  color: var(--ink);
}

.bleed-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 12px 8px;
  border: none;
  background: none;
  cursor: pointer;
  font: inherit;
  text-align: center;
  color: var(--ink);
}

.bleed-btn:hover .radio { background: var(--hot); }
.bleed-btn.selected .radio { background: var(--ink); }
.bleed-btn.selected .sides-label { text-decoration: underline; }

/* Front/back colour pickers stack with a labelled divider */
.colour-side + .colour-side {
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}

.sides-btn:hover .radio { background: var(--hot); }
.sides-btn.selected .radio { background: var(--ink); }
.sides-btn.selected .sides-label { text-decoration: underline; }

.sides-label { font-weight: 700; font-size: 15px; }

/* Orientation selector */
.orientation-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.orientation-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 12px 8px;
  border: none;
  background: none;
  cursor: pointer;
  font: inherit;
  text-align: center;
  color: var(--ink);
}

.orientation-btn:hover .radio { background: var(--hot); }
.orientation-btn.selected .radio { background: var(--ink); }
.orientation-btn.selected .orientation-label { text-decoration: underline; }

.orientation-label { font-weight: 700; font-size: 15px; }

/* Delivery selector */
.delivery-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 10px;
}

.delivery-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 12px 8px;
  border: none;
  background: none;
  cursor: pointer;
  font: inherit;
  text-align: center;
  color: var(--ink);
}

.delivery-btn:hover .radio { background: var(--hot); }
.delivery-btn.selected .radio { background: var(--ink); }
.delivery-btn.selected .delivery-label { text-decoration: underline; }

.delivery-label { font-weight: 700; font-size: 15px; }
.delivery-hint { font-size: 12px; color: var(--muted); line-height: 1.1; }

/* Cover type selector */
.cover-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.cover-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 12px 8px;
  border: none;
  background: none;
  cursor: pointer;
  font: inherit;
  text-align: center;
  color: var(--ink);
}

.cover-btn:hover .radio { background: var(--hot); }
.cover-btn.selected .radio { background: var(--ink); }
.cover-btn.selected .cover-label { text-decoration: underline; }

.cover-label { font-weight: 700; font-size: 15px; }
.cover-hint { font-size: 12px; color: var(--muted); line-height: 1.1; }

/* Binding selector */
/* Five bindings now, so let them flow rather than forcing two columns */
.binding-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 10px;
}

.binding-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 12px 8px;
  border: none;
  background: none;
  cursor: pointer;
  font: inherit;
  text-align: center;
  color: var(--ink);
}

.binding-btn:hover .radio { background: var(--hot); }
.binding-btn.selected .radio { background: var(--ink); }
.binding-btn.selected .binding-label { text-decoration: underline; }

.binding-icon {
  display: block;
  width: auto;
  height: 62px;
  margin: 0 auto;
}
.binding-label { font-weight: 700; font-size: 15px; }
.binding-hint { font-size: 12px; color: var(--muted); line-height: 1.1; }

/* Paper picker - family, then shade, then weight.
 * Swatch photography comes from the /paper page on pagemasters.co. */
.paper-stage + .paper-stage { margin-top: 20px; }

.stage-label {
  font-size: 15px;
  font-weight: 700;
  margin-bottom: 10px;
}

.paper-family-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(112px, 1fr));
  gap: 6px;
}

/* Now the photo sits inside a drawn sheet, the frame is part of the icon -
   so these use the same borderless + radio treatment as everything else. */
.paper-family-btn,
.paper-shade-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 8px 4px;
  border: none;
  background: none;
  cursor: pointer;
  font: inherit;
  text-align: center;
  color: var(--ink);
  height: 100%;
  justify-content: flex-start;
}

.paper-family-btn .radio,
.paper-shade-btn .radio { margin-top: auto; }

.paper-family-btn:hover .radio,
.paper-shade-btn:hover .radio { background: var(--hot); }

.paper-family-btn.selected .radio,
.paper-shade-btn.selected .radio { background: var(--ink); }

.paper-family-btn.selected .paper-family-name,
.paper-shade-btn.selected .paper-shade-name { text-decoration: underline; }

.paper-swatch { height: 84px; }
.paper-shade-btn .paper-swatch { height: 66px; }

.paper-family-name,
.paper-shade-name {
  font-weight: 700;
  font-size: 14px;
  line-height: 1.1;
}

.paper-from {
  font-size: 12px;
  color: var(--muted);
  padding: 0 4px 4px;
}

.paper-shade-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(86px, 1fr));
  gap: 4px;
  max-height: 340px;
  overflow-y: auto;
  padding-right: 4px;
}

.paper-shade-name { font-size: 12px; padding-bottom: 4px; }

.paper-weight-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 22px;
}

.paper-weight-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 8px 4px;
  border: none;
  background: none;
  cursor: pointer;
  font: inherit;
  color: var(--ink);
}

.paper-weight-gsm { font-weight: 700; font-size: 15px; }
.paper-weight-price { font-size: 12px; color: var(--muted); }

.paper-weight-btn:hover .radio { background: var(--hot); }
.paper-weight-btn.selected .radio { background: var(--ink); }
.paper-weight-btn.selected .paper-weight-gsm { text-decoration: underline; }

/* Colour selector
 * Follows the ink filter on pagemasters.co: faceted ink diamonds with a small
 * round radio underneath doing the selecting. No card, no box - the radio is
 * the whole affordance.
 */
.colour-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 20px;
  align-items: flex-start;
}

.colour-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 10px 4px;
  border: none;
  background: none;
  cursor: pointer;
  font: inherit;
  text-align: center;
  color: var(--ink);
}

.ink-stack {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
}

/* Diamonds overlap slightly, the way inks sit on the filter row */
.ink {
  width: 44px;
  height: 44px;
  display: block;
  flex: none;
}

.ink + .ink { margin-left: -14px; }

.ink-custom { color: var(--muted); }

.colour-btn .colour-label {
  font-weight: 700;
  font-size: 15px;
  line-height: 1.1;
}

/* Hints wrap to different line counts, so push the radio to the bottom of
   whichever button it's in - otherwise the row of radios sits ragged. */
.product-card, .size-btn, .sides-btn, .bleed-btn, .orientation-btn,
.binding-btn, .cover-btn, .delivery-btn, .colour-btn, .paper-weight-btn {
  height: 100%;
  justify-content: flex-start;
}

.product-card .radio, .size-btn .radio, .sides-btn .radio, .bleed-btn .radio,
.orientation-btn .radio, .binding-btn .radio, .cover-btn .radio,
.delivery-btn .radio, .colour-btn .radio, .paper-weight-btn .radio {
  margin-top: auto;
  padding-top: 0;
}

/* Their round radio: 16px, 2px black, filled when chosen */
.radio {
  width: 16px;
  height: 16px;
  border: 2px solid var(--ink);
  border-radius: 50%;
  background: none;
  flex: none;
  transition: background 0.12s;
}

.colour-btn:hover .radio { background: var(--hot); }
.colour-btn.selected .radio { background: var(--ink); }

.colour-btn.selected .colour-label { text-decoration: underline; }

.colour-hint {
  font-size: 10px;
  color: var(--muted);
}

/* Sized by HEIGHT, not width. Portrait icons use a 100x140 viewBox and
   landscape ones 140x100, so a shared width rendered the landscape ones at
   half the height - "2 sides" and "landscape" looked shrunken next to the
   rest. Height is the dimension the eye reads along a row of options. */
.pm-icon {
  display: block;
  width: auto;
  height: 52px;
  margin: 0 auto;
}

.icon-slot { display: block; }

.product-card .pm-icon { height: 58px; }

/* The one deliberate exception: paper sizes share a 100x140 canvas and vary
   the rectangle drawn inside it, so a fixed canvas height is what lets A6
   read as smaller than A3. */
.size-btn .pm-icon { height: 62px; }

.product-label {
  font-weight: 700;
  font-size: 14px;
}

.product-hint {
  font-size: 11px;
  color: var(--muted);
}

/* Step modules (product-specific) */
/* Steps sit one level down from modules - lighter rule, smaller heading */
.step-module {
  border: 10px solid var(--ink);
  border-image: var(--rope-ink) 20 round;
  padding: 14px;
  margin-bottom: 12px;
  /* Opaque - the gems pass behind these rather than through them, and the
     rope's casing is painted in this colour */
  background: var(--paper);
}

.step-module h3 {
  font-size: 21px;
  line-height: 1.1;
  font-weight: 700;
  margin-bottom: 4px;
}

.step-description {
  font-size: 15px;
  font-style: italic;
  color: var(--muted);
  margin-bottom: 14px;
  max-width: 58ch;
}

/* Forms */
.field-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

.field-grid.single { grid-template-columns: 1fr; }
.field-grid.triple { grid-template-columns: 1fr 1fr 1fr; }

label {
  display: block;
  font-size: 15px;
  font-weight: 700;
  margin-bottom: 4px;
}

input[type="number"],
input[type="text"],
select,
textarea {
  width: 100%;
  padding: 8px 10px;
  border: var(--rule-weight) solid var(--ink);
  background: var(--field);
  color: var(--ink);
  font: inherit;
  font-size: 16px;
  /* Chrome ignores an author line-height on a select while appearance is auto,
     so it computed ~5px taller than a text input beside it. An explicit height
     is the reliable way to keep a row of fields level. */
  line-height: 1.1;
  height: 40px;
}

textarea {
  height: auto;
  min-height: 96px;
}

input:focus, select:focus, textarea:focus {
  outline: var(--rule-weight) solid var(--hot);
  outline-offset: 1px;
}

.field-hint {
  display: block;
  font-size: 13px;
  color: var(--muted);
  margin-top: 3px;
}

.field-error {
  margin-top: 10px;
  font-size: 15px;
  font-weight: 700;
  color: var(--accent);
}

/* Range slider */
.range-full {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 6px;
  background: var(--line);
  outline: none;
  border: none;
  padding: 0;
  margin: 8px 0 4px;
  cursor: pointer;
}

/* The slider is a wide track, so a focus ring around the whole thing looks
   like an error state. Colour the handle instead. */
.range-full:focus,
.range-full:focus-visible { outline: none; }

.range-full::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 24px;
  height: 24px;
  background: var(--ink);
  border-radius: 50%;
  cursor: pointer;
}

.range-full::-moz-range-thumb {
  width: 24px;
  height: 24px;
  background: var(--ink);
  border: none;
  border-radius: 50%;
  cursor: pointer;
}

/* :focus rather than :focus-visible - the handle should colour whether it's
   reached by keyboard or grabbed with the mouse */
.range-full:hover::-webkit-slider-thumb,
.range-full:focus::-webkit-slider-thumb,
.range-full:active::-webkit-slider-thumb { background: var(--hot); }

.range-full:hover::-moz-range-thumb,
.range-full:focus::-moz-range-thumb,
.range-full:active::-moz-range-thumb { background: var(--hot); }

.range-labels {
  display: flex;
  justify-content: space-between;
  font-size: 11px;
  color: var(--muted);
}

/* Paper info */
.paper-info-card {
  margin-top: 12px;
  padding: 8px;
  background: var(--tint);
  font-size: 13px;
}

.paper-info-card strong { display: block; margin-bottom: 2px; }

/* Finishing */
.finishing-item {
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}

.finishing-item:last-child { border-bottom: none; }

.finishing-toggle {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  font-weight: 700;
  font-size: 14px;
}

.finishing-toggle input[type="checkbox"] { width: 16px; height: 16px; }

.finishing-hint {
  font-size: 12px;
  color: var(--muted);
  margin-left: 22px;
  margin-top: 2px;
}

.finishing-fields {
  margin-top: 8px;
  padding-left: 22px;
}

.finishing-fields .field-row { display: flex; gap: 10px; }
.finishing-fields .field-row label { flex: 1; }

.finishing-subtotal {
  font-weight: 700;
  margin-top: 6px;
}

/* Booklet page select */
.page-select-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 6px;
  margin-bottom: 12px;
}

.page-select-btn {
  padding: 8px 4px;
  border: 1px solid var(--line);
  background: var(--paper);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  text-align: center;
}

.page-select-btn:hover { border-color: var(--hot); }
/* Dense value grid, so this keeps the chip treatment rather than a radio -
   but selected reads the same solid black as a filled radio does */
.page-select-more {
  grid-column: span 2;
  font-style: italic;
}

.page-select-btn.selected { background: var(--ink); color: var(--paper); border-color: var(--ink); }

/* Review */
.review-summary { margin: 12px 0; }

.review-row {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 6px 0;
  border-bottom: 1px solid var(--line);
  font-size: 16px;
}

.review-label { color: var(--muted); }
.review-value { font-weight: 700; text-align: right; }

.review-note {
  margin-top: 14px;
  padding: 12px 14px;
  background: var(--tint);
  border-left: 3px solid var(--hot);
  font-size: 15px;
  line-height: 1.1;
  color: var(--muted);
}

/* Results */
.results-card {
  border: 12px solid var(--ink);
  border-image: var(--rope-ink) 20 round;
  padding: 14px;
  /* Enough gap that this card's rope and the review section's above it read as
     two borders rather than one muddled band */
  margin: 16px 0 12px;
  background: var(--paper);
}

.result-row {
  display: flex;
  justify-content: space-between;
  padding: 6px 0;
  font-size: 16px;
  border-bottom: 1px solid var(--line);
}

.result-row:last-child { border-bottom: none; }

.result-row.total {
  border-top: var(--rule-weight) solid var(--ink);
  border-bottom: none;
  padding-top: 12px;
  margin-top: 8px;
  font-size: 25px;
  line-height: 1.1;
  font-weight: 700;
}

.result-value { font-weight: 700; }

/* The number they came for - and the "Get in touch" that replaces it when we
   can't price the job. Magenta, the same one the icons and focus states use. */
.result-row.total .result-value { color: var(--hot); }

/* Running Total */
.running-total {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background: var(--ink);
  color: var(--paper);
  padding: 12px 16px;
  font-size: 18px;
  display: flex;
  justify-content: center;
  z-index: 10;
}

.running-total strong { color: var(--hot); }

/* Cost per unit - a footnote to the total rather than another cost line, so
   it sits quieter and carries no top rule. */
.result-row.per-unit {
  border-top: none;
  padding-top: 2px;
  font-size: 15px;
  color: var(--muted);
}
.result-row.per-unit .result-value { color: var(--muted); font-weight: 400; }

.note-per-unit {
  font-size: 12px;
  color: var(--muted);
  text-align: right;
  margin-top: 2px;
}

/* Buttons */
.btn-row { display: flex; gap: 10px; margin-top: 16px; flex-wrap: wrap; }

.btn {
  padding: 10px 20px;
  border: var(--rule-weight) solid var(--ink);
  font: inherit;
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
  background: var(--field);
  color: var(--ink);
  transition: background 0.15s, color 0.15s;
}

.btn:hover { background: var(--hot); border-color: var(--hot); color: #fff; }

.btn-primary { background: var(--ink); color: var(--paper); }
.btn-primary:hover { background: var(--hot); border-color: var(--hot); color: #fff; }

.btn-secondary { background: var(--field); color: var(--ink); }

.btn-sm { padding: 4px 12px; font-size: 14px; border-width: 1px; }

.btn-load { background: var(--field); }
.btn-pdf { background: var(--ink); color: var(--paper); }
.btn-delete { background: var(--field); color: var(--accent); border-color: var(--accent); }
.btn-delete:hover { background: var(--accent); color: #fff; }

/* History */
.history-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  border: 1px solid var(--line);
  margin-bottom: 6px;
}

.history-info strong { display: block; }
.history-date { font-size: 12px; color: var(--muted); }
.history-summary { font-size: 13px; color: var(--muted); }

.history-actions { display: flex; gap: 4px; margin-left: 10px; }

.history-empty { text-align: center; color: var(--line); padding: 32px 0; }

/* Toast */
.toast {
  position: fixed;
  bottom: 50px;
  left: 50%;
  transform: translateX(-50%) translateY(80px);
  background: var(--ink);
  color: var(--paper);
  padding: 8px 20px;
  font: inherit;
  font-size: 14px;
  opacity: 0;
  transition: transform 0.2s, opacity 0.2s;
  z-index: 1000;
}

.toast.show {
  transform: translateX(-50%) translateY(0);
  opacity: 1;
}

/* Other product textarea */
textarea {
  width: 100%;
  padding: 6px;
  border: 1px solid var(--line);
  font: inherit;
  font-size: 14px;
  min-height: 80px;
  resize: vertical;
}

textarea:focus {
  outline: 2px solid var(--ink);
  outline-offset: -1px;
}

/* Debug post-it */
/* "Your estimate" note - customer-facing, pinned left.
 * Needs room beside the 720px body, so it only appears once the viewport is
 * wide enough; below that the fixed bottom bar carries the running total. */
/* Gem illustrations scattered behind the page.
 *
 * One absolutely-positioned layer spanning the viewport, filled by
 * fillGemField(). It sits at z-index 0 with .container lifted to 1, so the
 * gems pass behind the form. The module boxes carry an opaque background for
 * exactly this reason - a gem slides under one and reappears the other side.
 *
 * Clipped, so the field can be over-filled and still never make the page
 * scroll further than the form itself. */
body { position: relative; }

/* Lifted above .gem-field, which sits at z-index 0 */
.container { position: relative; z-index: 1; }

.gem-field {
  display: none;
  position: absolute;
  top: 0;
  bottom: 0;
  /* Body is a 720px centred column; the gems want the whole window. The width
     is set from clientWidth in JS - 100vw includes the scrollbar, which would
     push the field past the page edge and add a horizontal scroll. */
  left: 50%;
  width: 100vw;
  transform: translateX(-50%);
  z-index: 0;
  pointer-events: none;
  user-select: none;
  overflow: hidden;
}

.gem-field img {
  position: absolute;
  height: auto;
  opacity: 0.85;
}

/* Below this the form fills the window and the gems would be almost entirely
   hidden behind it - not worth the download. */
@media (min-width: 1024px) {
  .gem-field { display: block; }
}

/* Narrow screens have no room beside the form, so the note becomes a normal
 * block at the end of the page. It still has to be reachable - it's the only
 * route to saved estimates. */
.estimate-note {
  display: block;
  position: relative;
  max-width: 720px;
  margin: 0 auto 100px;
  padding: 18px 18px 22px;
  color: var(--ink);

  /* Sticky-note paper. No border - the shadow and the tilt do that work. */
  background: #fff09a;
  box-shadow:
    0 1px 1px rgba(96, 78, 18, 0.10),
    0 8px 16px -6px rgba(96, 78, 18, 0.30);

  /* Re-tint the children for the yellow ground. The globals stay untouched.
     Pure magenta manages only 2.7:1 on this yellow, so it deepens here - same
     pink, enough contrast to read. */
  --line: rgba(90, 72, 16, 0.26);
  --muted: #6e5c22;
  --accent: #b4340f;
  --hot: #d100d1;
}

@media (prefers-reduced-motion: reduce) {
  .estimate-note { transform: none; }
  [class$="-grid"] > :not(.selected) { transition: none; }
}

/* A wider column once the screen can take it. The pinned note is 250px at
   right: 28px, so the body can only grow while (viewport - body) / 2 stays
   clear of 278px plus a margin - hence the breakpoints being this late. */
@media (min-width: 1440px) {
  body { max-width: 840px; font-size: 19px; }
  .module-section h2 { font-size: 27px; }
  .step-module h3 { font-size: 23px; }
}

@media (min-width: 1800px) {
  body { max-width: 920px; }
}

@media (min-width: 1120px) {
  /* Pinned: this is where it reads as a note stuck to the screen, so it gets
     the tilt. Full-width at narrow sizes it would just look skewed. */
  .estimate-note {
    position: fixed;
    top: 28px;
    right: 28px;
    width: 250px;
    max-width: none;
    margin: 0;
    max-height: calc(100vh - 56px);
    overflow-y: auto;
    z-index: 50;
    transform: rotate(-1.4deg);
  }
  /* The note carries the running total once it's pinned */
  .running-total { display: none; }
  body { padding-bottom: 40px; }
}

.estimate-note h2 {
  font-size: 17px;
  font-weight: 700;
  margin-bottom: 10px;
  padding-bottom: 8px;
  border-bottom: 2px solid var(--ink);
}

.note-spec { margin-bottom: 12px; }

.note-spec > div {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 3px 0;
  font-size: 13px;
}

.note-spec dt { color: var(--muted); }
.note-spec dd { font-weight: 700; text-align: right; }

.note-costs {
  border-top: 1px solid var(--line);
  padding-top: 8px;
}

.note-cost {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  font-size: 13px;
  padding: 3px 0;
  font-variant-numeric: tabular-nums;
}

.note-cost span:first-child { color: var(--muted); }
.note-cost span:last-child { font-weight: 700; }

.note-total {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
  margin-top: 8px;
  padding-top: 10px;
  border-top: 2px solid var(--ink);
  font-weight: 700;
}

.note-total-value {
  font-size: 21px;
  color: var(--hot);
  font-variant-numeric: tabular-nums;
}

.estimate-note.is-blocked .note-total-value { font-size: 15px; }

.note-caveat {
  font-size: 11px;
  color: var(--muted);
  margin-top: 6px;
  line-height: 1.1;
}

.note-actions {
  display: flex;
  gap: 6px;
  margin-top: 12px;
}

.note-actions .btn { flex: 1; padding: 6px 8px; }

.note-actions .btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.note-actions .btn:disabled:hover {
  background: var(--field);
  color: var(--ink);
}

.note-actions .btn-primary:disabled:hover {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

.note-saved-link {
  display: block;
  width: 100%;
  margin-top: 8px;
  padding: 6px 0;
  border: none;
  background: none;
  font: inherit;
  font-size: 13px;
  color: var(--muted);
  text-decoration: underline;
  cursor: pointer;
}

.note-saved-link:hover:not(:disabled) { color: var(--hot); }
.note-saved-link:disabled { opacity: 0.45; cursor: default; text-decoration: none; }

/* Nothing chosen yet - the note explains itself rather than showing zeroes */
.note-empty {
  margin-top: 10px;
  font-size: 13px;
  color: var(--muted);
  line-height: 1.1;
}

.estimate-note.is-empty .note-total,
.estimate-note.is-pending .note-total { opacity: 0.4; }

.debug-postit {
  position: fixed;
  top: 16px;
  left: 16px;
  background: #dfe9ef;
  border: 1px solid #a9bfcc;
  padding: 10px 14px;
  font-family: monospace;
  font-size: 11px;
  line-height: 1.25;
  /* Wide enough for the longest working-out line to sit on one row - the book
     branch prints things like "Passes: 5 x (ceil(4/2)x2) = 20". Capped against
     the viewport so it can't run off a narrow window. */
  width: min(400px, calc(100vw - 32px));
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  box-shadow: 2px 2px 6px rgba(0,0,0,0.15);
  z-index: 100;
}

.debug-postit strong {
  display: block;
  font-size: 12px;
  margin-bottom: 4px;
  border-bottom: 1px solid #a9bfcc;
  padding-bottom: 4px;
}

.debug-postit pre {
  margin: 0;
  /* break-all used to split figures mid-number when the panel was narrow.
     There's room now, so wrap at word boundaries and keep them readable. */
  white-space: pre-wrap;
  overflow-wrap: break-word;
}

@media (max-width: 768px) {
  .debug-postit { display: none !important; }
}

@media (max-width: 600px) {
  .field-grid { grid-template-columns: 1fr; }
  .product-grid { grid-template-columns: 1fr; }
  body { font-size: 17px; }
  .logo-mark { width: 76px; }
  .module-section { border-width: 8px; padding: 14px; }
  .step-module { border-width: 8px; padding: 12px; }
  .module-section h2 { font-size: 21px; }
  .step-module h3 { font-size: 19px; }
  .size-grid { grid-template-columns: repeat(2, 1fr); }
  .sides-grid, .orientation-grid, .cover-grid { grid-template-columns: 1fr 1fr; }
  .binding-grid { grid-template-columns: repeat(3, 1fr); }
  .paper-family-grid { grid-template-columns: repeat(2, 1fr); }
  .paper-shade-grid { grid-template-columns: repeat(3, 1fr); max-height: 260px; }
  .result-row.total { font-size: 21px; }
  .delivery-grid { grid-template-columns: 1fr; }
  .page-select-grid { grid-template-columns: repeat(4, 1fr); }
  /* The colour picker is a flex row - let it wrap and tighten the gaps
     rather than forcing columns on it */
  .colour-grid { gap: 0 12px; justify-content: space-between; }
  .ink { width: 34px; height: 34px; }
  .ink + .ink { margin-left: -12px; }
  .history-item { flex-direction: column; align-items: flex-start; }
  .history-actions { margin-left: 0; margin-top: 6px; }
  .btn-row { flex-direction: column; }
}
