/* =========================================================
   shop/shop.css: layer 4, the configurator and shop-app rules.
   Loads after stark.css, store.css (never edited) and
   css/shop-prototype.css (the prototype's shop layer, copied
   unchanged from shop-prototype/css/shop.css).
   Colours and fonts are the prototype's tokens only
   (--navy, --red, --yellow, --cyan, --shop-*): no new hex values.
   ========================================================= */

/* ---- Landing: tiles not yet orderable online stay visible but quiet ---- */
.sgcard--soon { cursor: default; }
/* Polish P1-1: the "order at the counter" line reads quieter than the
   yellow "Set it up" of a tile that can be ordered online. P2-4: a soon
   tile is a <div>, not a link, so it does not lift on hover either. */
.shop .sgcard--soon .sgcard__go { color: rgba(255,255,255,.72); }
.shop .sgcard--soon .sgcard__go::after { content: none; }
.shop .sgcard--soon:hover { translate: none; box-shadow: none; }

/* ---- Landing tiles, legibility (QC 27 Sep). stark.css pins the two badges
   absolutely in the top corners, so on a narrow card (and on a long title
   at 1440) they ran into each other and over the title. Here they join the
   card's flow in their own row at the top: they wrap instead of colliding,
   and the title always starts below them. The text sits on a solid navy
   band, so no photo can wash it out; badges get solid backgrounds too. ---- */
.shop .sgcard { justify-content: flex-start; gap: .9rem; }
.shop .sgcard__badges { display: flex; flex-wrap: wrap; justify-content: space-between; gap: .45rem; margin: 0 0 auto; position: relative; z-index: 1; }
.shop .sgcard__badges > .sgcard__tag,
.shop .sgcard__badges > .sgcard__count { position: static; backdrop-filter: none; background: rgba(2,14,43,.92); }
.shop .sgcard__badges > .sgcard__tag { color: #bfeaff; border-color: rgba(127,216,255,.45); text-shadow: none; }
.shop .sgcard--soon .sgcard__badges > .sgcard__tag { color: #fff; border-color: rgba(255,255,255,.4); }
.shop .sgcard__body {
  position: relative; z-index: 1; margin: 0 -.35rem -.35rem; padding: .85rem .9rem .8rem;
  border-radius: 14px; background: rgba(2,14,43,.88); box-shadow: 0 8px 24px -12px rgba(0,0,0,.6);
}
.shop .sgcard__body h3 { overflow-wrap: anywhere; }
.shop .sgcard__body p { color: rgba(255,255,255,.92); max-width: none; }
.shop .sgcard__body .sgcard__from { color: rgba(255,255,255,.92); }
/* Polish P2-2: a taller card and a two-line blurb, so the photo shows
   above the text band instead of about a fifth of it. */
.shop .sgcard { min-height: 22rem; }
/* Finish review 27 Sep: 22rem stays on desktop; one tile to a row on a
   phone stacked nine of them to 4 618px, so below 780px the tiles are
   shorter and closer together (the photo still shows above the text band). */
@media (max-width: 780px) {
  .shop .sgcard { min-height: 17rem; }
  .shop .sgrid { gap: 1rem; }
}
/* A last tile alone on its row runs full width (index.php); on the
   two-column grid it goes back to one column when it has a partner there. */
@media (max-width: 1080px) {
  .shop .sgcard--half-md { grid-column: span 1; }
}
.shop .sgcard__body p { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
/* Polish P3-6: no lone words on the hero lead's last line. "pretty" left
   "you go." alone at 1440 (Chrome only lifts a single orphan word), so the
   two lines are balanced instead. */
.shop .page-hero__lead { text-wrap: balance; }
/* Contrast table: breadcrumb at 55% white on the header navy was 4.22:1
   at 11px; 70% passes. */
.shop .page-hero .crumbs li { color: rgba(255,255,255,.7); }

/* ---- Product intro: the photo well stays short so the questions start
   near the fold (the configurator is the page, not the photo) ---- */
.cfg-intro .pgal__main { aspect-ratio: 4 / 3; }
.cfg-from__suffix { font-family: var(--font-b); font-size: .95rem; color: var(--shop-ink-soft); font-weight: 700; }

/* ---- Configurator layout: questions left, price right ---- */
.cfg-sec { padding-top: clamp(1.4rem, 3vw, 2.2rem); }
/* Polish P1-7: stark.css gives every section overflow: hidden, which makes
   this one a scroll container and cancels the price panel's sticky top.
   clip still trims the texture but is not a scroll container. */
.shop .cfg-sec { overflow: clip; }
.cfglay { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 2rem; align-items: start; }
.cfg__title {
  font-family: var(--font-d); text-transform: uppercase; color: var(--navy);
  font-size: clamp(1.6rem, 3vw, 2.1rem); line-height: 1; margin: 0 0 1.2rem;
}

/* One question per line: a numbered label, then its answers on one row. */
.cfg-q { padding: 1.1rem 0 1.2rem; border-top: 1px solid var(--shop-line); margin: 0; }
.cfg-q__label {
  display: flex; align-items: center; gap: .6rem; padding: 0; margin: 0 0 .75rem;
  font-family: var(--font-d); text-transform: uppercase; font-size: 1.3rem; line-height: 1; color: var(--navy);
}
.cfg-q__n {
  display: inline-grid; place-items: center; width: 1.6rem; height: 1.6rem; flex: none; border-radius: 50%;
  background: var(--navy); color: #fff; font-family: var(--font-m); font-size: .8125rem;
}
/* Every question heading reads the same (QC 27 Sep). A choice question's
   heading is a <legend> inside .mopts, and shop-prototype.css styles
   ".mopts legend" as a small spaced caption, which beat .cfg-q__label; this
   wins it back so Pages, Copies, Size, Colour, Sides, Binding and Covers all
   share the one heading. The fieldset stops being a grid, so no extra gap
   opens under the heading either. */
.cfg .cfg-q.mopts { display: block; }
.cfg .mopts > legend.cfg-q__label {
  display: flex; align-items: center; gap: .6rem; padding: 0; margin: 0 0 .75rem;
  font-family: var(--font-d); text-transform: uppercase; font-size: 1.3rem; line-height: 1;
  letter-spacing: normal; color: var(--navy);
}
.cfg-q__help { margin: -.3rem 0 .7rem; }
/* Polish P2-9: a fieldset draws its top rule through the legend, so Size,
   Colour, Sides, Binding and Covers had a rule running out to the right of
   the heading while Pages and Copies had a full rule above. A floated
   legend is not drawn in the border: every question gets one rule above. */
.cfg fieldset.cfg-q > legend.cfg-q__label { float: left; width: 100%; }
.cfg fieldset.cfg-q > legend + * { clear: both; }

/* Polish P2-11: small print no smaller than 13px (help lines, drop hint,
   price panel fine print), and the Files / Collection / Checked labels 12px. */
.cfg-sum__fine, .cfg .field__hint, .cfg-drop__hint, .cfg-q__help { font-size: .8125rem; }   /* re-skin 23 Oct (spec 18): 13px floor */
.shop .psum__meta b { font-size: .8125rem; }

/* Polish P2-10 and the contrast table: the steppers are 44px tall, their
   focus ring sits inside the pill (the pill clips anything outside), and
   the border reads at 3.35:1 instead of 1.53:1. */
.shop .qty { border-color: rgba(0,49,121,.55); }
.shop .qty button, .shop .qty input { min-height: 44px; }
.shop .qty :focus-visible { outline-offset: -3px; }

/* Polish P1-8: the Files / Collection / Checked list. On a wider screen the
   summary line is hidden and the list shows as before; on a phone it folds
   (configurator.js closes it) under a plain summary line. */
.cfg-meta > summary {
  display: flex; align-items: center; gap: .45rem; min-height: 44px; padding-top: .6rem; border-top: 1px solid var(--shop-line);
  cursor: pointer; list-style: none; font-size: .86rem; font-weight: 800; color: var(--navy);
}
.cfg-meta > summary::-webkit-details-marker { display: none; }
.cfg-meta > summary::after,
.cfg-link > summary::after {
  content: ""; width: .45rem; height: .45rem; border: solid currentColor; border-width: 0 2px 2px 0; transform: rotate(45deg) translateY(-2px);
}
.cfg-meta[open] > summary::after,
.cfg-link[open] > summary::after { transform: rotate(-135deg) translateY(-1px); }
.cfg-meta[open] > summary + .psum__meta { border-top: 0; padding-top: .2rem; }
@media (min-width: 781px) {
  .cfg-meta > summary { display: none; }
  .cfg-meta[open] > summary + .psum__meta { border-top: 1px solid var(--shop-line); padding-top: 1.1rem; }
}

/* Polish P3-13: "Big file? Paste a link instead" folds under the drop box. */
.cfg-link > summary {
  display: inline-flex; align-items: center; gap: .45rem; min-height: 44px; cursor: pointer; list-style: none;
  font-size: .86rem; font-weight: 800; color: var(--navy); text-decoration: underline; text-underline-offset: 3px;
}
.cfg-link > summary::-webkit-details-marker { display: none; }
.cfg-link > summary:focus-visible, .cfg-meta > summary:focus-visible { outline: 3px solid rgba(0,174,239,.85); outline-offset: 2px; border-radius: 6px; }
.cfg-link .field { margin: .3rem 0 0; }

.cfg-opts { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: .6rem; }
/* The prototype's .mopt is a three-column row for delivery methods; here it
   is a compact answer tile: radio + name on one line, price effect under. */
.cfg-opt { grid-template-columns: auto 1fr; gap: .15rem .6rem; padding: .75rem .9rem; align-items: center; }
.cfg-opt input { margin: 0; }
.cfg-opt .mopt__price { grid-column: 2; font-size: .8125rem; font-weight: 700; color: var(--shop-ink-soft); }
.cfg-opt .mopt__price--free { color: var(--shop-ink-soft); font-weight: 600; }
.cfg-opt:has(input:checked) .mopt__price { color: var(--red); }
.cfg-opt:has(input:checked) .mopt__price--free { color: var(--shop-ink-soft); }

.cfg-num { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1rem; }
.cfg-num .qty input { width: 76px; }
.cfg-num .field__hint { margin: 0; }

/* ---- Short live lines under a question (double-sided, ring fit) and the
   Binding add-on (ruling 27 Sep) ---- */
.cfg [hidden] { display: none !important; }
.cfg-note { margin: .65rem 0 0; font-size: .86rem; font-weight: 700; color: var(--navy); }
.cfg-note[data-state="warn"] { color: var(--red); }
.cfg-q__opt { font-family: var(--font-b); text-transform: none; font-size: .8125rem; font-weight: 700; color: var(--shop-ink-soft); }
.cfg-ring { margin: .8rem 0 0; display: grid; gap: .35rem; max-width: 420px; }
.cfg-ring__label { font-size: .82rem; font-weight: 800; color: var(--navy); }
.cfg-ring__select {
  width: 100%; min-height: 44px; padding: .55rem .75rem; font: inherit; font-size: .92rem; color: var(--navy);
  background: var(--shop-card-solid); border: 1px solid var(--shop-line-hard); border-radius: 12px;
}
.cfg-ring__select:focus-visible { outline: 3px solid rgba(0,174,239,.85); outline-offset: 2px; }

/* ---- Collection and delivery on the Added / basket page ---- */
.cfg-ful { margin-top: 1.2rem; padding-top: 1rem; border-top: 1px solid var(--shop-line); }
.cfg-ful__title { font-family: var(--font-d); text-transform: uppercase; font-size: 1.05rem; line-height: 1; color: var(--navy); margin: 0 0 .5rem; }
.cfg-ful__lines li { display: grid; gap: .15rem; font-size: .86rem; }
.cfg-ful__lines li span { color: var(--shop-ink-soft); }
/* Polish P3-22: opening hours, one row a line. */
.cfg-ful__lines .cfg-ful__hours { list-style: none; margin: .35rem 0 0; padding: 0; display: grid; gap: .2rem; }
.cfg-ful__lines .cfg-ful__hours li { display: flex; justify-content: space-between; gap: .8rem; font-size: .86rem; }
.cfg-ful__lines .cfg-ful__hours li b { color: var(--navy); font-weight: 700; }

/* ---- Upload drop area ---- */
.cfg-drop {
  position: relative; display: grid; justify-items: center; gap: .35rem; text-align: center;
  padding: 1.6rem 1rem; border-radius: var(--shop-radius); cursor: pointer;
  border: 2px dashed var(--shop-line-hard); background: rgba(255,255,255,.7);
  transition: border-color .18s ease, background .18s ease;
}
.cfg-drop:hover, .cfg-drop.is-over { border-color: rgba(0,174,239,.85); background: #fff; }
.cfg-drop.has-file { border-style: solid; border-color: var(--red); background: #fff; }
.cfg-drop__input { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; }
.cfg-drop__input:focus-visible + .cfg-drop__icon { outline: 3px solid rgba(0,174,239,.85); outline-offset: 4px; border-radius: 50%; }
.cfg-drop__icon { width: 44px; height: 44px; display: grid; place-items: center; border-radius: 50%; background: var(--navy); color: #fff; }
.cfg-drop__icon svg { width: 22px; height: 22px; }
.cfg-drop__text { color: var(--navy); font-size: .98rem; }
.cfg-drop__btn { color: var(--red); font-weight: 800; text-decoration: underline; text-underline-offset: 3px; }
.cfg-drop__hint { font-size: .82rem; color: var(--shop-ink-soft); }   /* polish P2-11: 13px */
.cfg-read { margin: .7rem 0 0; font-size: .92rem; font-weight: 700; color: var(--navy); min-height: 1.4em; }
.cfg-read[data-state="error"] { color: var(--red); }
.cfg-read[data-state="busy"] { color: var(--shop-ink-soft); font-weight: 600; }
.cfg-link { margin: .6rem 0 0; }
.cfg-link label { font-size: .82rem; }

/* ---- Price panel ---- */
.cfg-sum__toggle { display: none; }
.cfg-sum { top: 96px; }
.cfg-sum__lines li { font-size: .86rem; }
.cfg-sum__units { font-size: .86rem; }
.cfg-sum__percopy { margin: .45rem 0 0; text-align: right; font-size: .9rem; color: var(--shop-ink-soft); }
.cfg-sum__percopy b { color: var(--navy); font-weight: 800; white-space: nowrap; }
.cfg-sum__err { margin: .6rem 0 0; font-size: .82rem; color: var(--red); font-weight: 700; }
/* Extras under the printing line: binding, covers, express (QC 27 Sep). */
.cfg-sum__extras { list-style: none; margin: 0; padding: 0; }
.cfg-sum__extras li { display: flex; justify-content: space-between; gap: .8rem; padding: .35rem 0; font-size: .86rem; border-top: 1px dashed var(--shop-line); }
.cfg-sum__extras li small { display: block; color: var(--shop-ink-soft); font-size: .8125rem; }
.cfg-sum__extras li b { white-space: nowrap; color: var(--navy); }
.cfg-sum [data-total] { transition: opacity .15s ease; }
.cfg-sum.is-pricing [data-total] { opacity: .45; }

/* ---- Notices (polish P1-18): stark's flex notice put the link in its own
   column, in the dark theme's light-blue link colour (1.42:1 on cream). ---- */
.shop .notice { display: block; }
.shop .notice a { color: var(--navy); font-weight: 800; text-decoration: underline; text-underline-offset: 3px; }

/* ---- Added / basket stand-in ---- */
.cfg-added, .cfg-basket { position: static; }
.cfg-added__tag { margin: 0 0 .3rem; }
.cfg-added__title { font-family: var(--font-d); text-transform: uppercase; color: var(--navy); font-size: 1.9rem; line-height: 1.05; margin: 0 0 1rem; }
.cfg-added .osum__lines small { font-weight: 600; color: var(--shop-ink-soft); margin-left: .3rem; }
.cfg-added__acts { display: flex; flex-wrap: wrap; gap: .7rem; margin-top: 1.2rem; }
/* Each button keeps its words on one line (QC 27 Sep: both wrapped at
   375); when both do not fit side by side they stack. */
.cfg-added__acts .btn { flex: 1 1 11rem; justify-content: center; white-space: nowrap; }
.cfg-basket .osum__lines li span { min-width: 0; }
/* Polish P3-21: one rule between the lines and the Total, not two. */
.cfg-basket .osum__lines { border-bottom: 0; }
/* Polish P2-19 and P1-17: each basket line is its name, then the job, the
   choices and the file on their own short lines; the price and a quiet
   Remove sit on the right. */
.cfg-basket .cfg-bline { align-items: flex-start; padding-bottom: .7rem; border-bottom: 1px solid var(--shop-line); }
.cfg-bline__what { display: grid; gap: .15rem; }
.cfg-bline__what b { color: var(--navy); }
.cfg-bline__what i { display: block; font-size: .86rem; }
.cfg-bline__file { overflow-wrap: anywhere; }
.cfg-basket .cfg-bline:last-child { border-bottom: 0; padding-bottom: 0; }
.cfg-bline__end { display: grid; justify-items: end; align-content: start; gap: .1rem; flex: none; }
.cfg-bline__rm { margin: 0; }
.cfg-remove {
  min-height: 44px; padding: 0 .1rem; border: 0; background: none; cursor: pointer;
  font: inherit; font-size: .82rem; font-weight: 800; color: var(--red);
  text-decoration: underline; text-underline-offset: 3px;
}
.cfg-remove:hover { color: var(--navy); }
.cfg-remove:focus-visible { outline: 3px solid rgba(0,174,239,.85); outline-offset: 2px; border-radius: 6px; }
/* Polish P3-23: a way back to the shop under the total. */
.cfg-basket__acts { display: flex; margin-top: 1.1rem; }
.cfg-basket__acts .btn { flex: 1 1 11rem; justify-content: center; white-space: nowrap; }
/* Brief M (QC 27 Sep): with extras chosen, the Added page's values ran off
   the card at 375 (392px in 375), because the prototype keeps every value on
   one line. Here a value may wrap, sits right under its label when space is
   short, and a price effect drops to its own line; a file name is cut short
   with an ellipsis (full name in its title). */
.cfg-added .osum__lines { grid-template-columns: minmax(0, 1fr); }
.cfg-added .osum__lines li { flex-wrap: wrap; gap: .15rem .8rem; min-width: 0; }
.cfg-added .osum__lines li > i { flex: 0 0 auto; }
.cfg-added .osum__lines li > b { flex: 1 1 10rem; min-width: 0; text-align: right; white-space: normal; overflow-wrap: anywhere; }
.cfg-added .osum__lines li > b small { display: block; margin-left: 0; white-space: normal; }
.cfg-added .osum__lines li.cfg-added__file > b { flex-basis: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; overflow-wrap: normal; }

/* ---- Tablet: the price panel drops under the questions ---- */
@media (max-width: 1080px) {
  .cfglay { grid-template-columns: 1fr; }
}

/* ---- Phone: the price sticks to the bottom of the screen with the
   button, so the total is never scrolled away (print ordering study). ---- */
@media (max-width: 780px) {
  .cfg-sum {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto; z-index: 40;
    border-radius: var(--shop-radius) var(--shop-radius) 0 0; padding: .75rem 1rem calc(.75rem + env(safe-area-inset-bottom));
    display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 0 .8rem;
    background: var(--shop-card-solid);
  }
  .cfg-sum h2, .cfg-sum__lines, .cfg-sum__working, .cfg-sum__fine { display: none; }
  /* Brief M: the bar's handle opens the working (printing + each extra)
     above the total, so a phone customer can see how the price adds up. */
  .cfg-sum { max-height: 75vh; overflow-y: auto; }
  .cfg-sum__toggle {
    display: flex; align-items: center; justify-content: center; gap: .45rem; grid-column: 1 / -1; grid-row: 1;
    width: 100%; min-height: 36px; margin: -.35rem 0 .35rem; padding: .2rem 0; border: 0; background: none; cursor: pointer;
    font: inherit; font-size: .82rem; font-weight: 800; color: var(--navy); text-decoration: underline; text-underline-offset: 3px;
  }
  .cfg-sum__toggle::after {
    content: ""; width: .5rem; height: .5rem; border: solid currentColor; border-width: 2px 2px 0 0; transform: rotate(-45deg) translateY(2px);
  }
  .cfg-sum.is-open .cfg-sum__toggle::after { transform: rotate(135deg) translateY(2px); }
  .cfg-sum.is-open .cfg-sum__working { display: block; grid-column: 1 / -1; grid-row: 2; margin: 0 0 .5rem; padding-bottom: .5rem; border-bottom: 1px solid var(--shop-line); }
  .cfg-sum.is-open .cfg-sum__working .cfg-sum__extras:not([hidden]) { display: block; }
  .cfg-sum__working .cfg-sum__units { font-size: .86rem; padding: .3rem 0; }
  .cfg-sum__total { grid-row: 3 !important; }
  .cfg-sum__percopy { grid-row: 4 !important; }
  .cfg-sum__err { grid-row: 5 !important; }
  .cfg-sum__cta { grid-row: 3 / span 2 !important; }
  .cfg-sum__total { margin: 0; padding: 0; border: 0; grid-column: 1; grid-row: 1; justify-content: flex-start; }
  .cfg-sum__total span { font-size: 1rem; }
  .cfg-sum__total b { font-size: 1.7rem; }
  .cfg-sum__percopy { grid-column: 1; grid-row: 2; margin: 0; text-align: left; font-size: .8125rem; }
  .cfg-sum__err { grid-column: 1 / -1; grid-row: 3; }
  .cfg-sum__cta { grid-column: 2; grid-row: 1 / span 2; margin: 0; width: auto; padding-inline: 1.2rem; }
  /* room under the footer so the bar never covers the last question
     (polish P3-16: the bar is 114px tall, so 124px) */
  /* Re-skin 23 Oct (spec A1, 20): the 124px used to pad the body, which left a pale strip under the footer.
     It now pads .ord-sec (the last question clears the bar) and the footer (the copyright line clears it);
     see the re-skin block at the end of this file. */
  /* Polish P1-8: a short photo and a three-line description, so "Set up
     your print job" starts within the first screen. */
  .cfg-intro .pgal__main { aspect-ratio: 16 / 9; max-height: 150px; width: 100%; }
  .cfg-intro .psum__short { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin-bottom: .8rem; }
  .shop .cfg-intro { gap: 1rem; }
  .shop .cfg-intro .psum__vat { margin-bottom: .6rem; }
  /* measured at 375x812: the intro section's 64px bottom padding and the
     setup section's top padding kept the title at y=744; the target is 620 */
  .shop .cfg-top { padding-bottom: 1rem; }
  .shop .cfg-sec { padding-top: .6rem; }
  .cfg-opts { grid-template-columns: 1fr 1fr; }
}

/* Polish P2-12: two answers a row on a small phone too (the longest,
   "Clear front and back cover", fits in two lines at 160px). */
@media (max-width: 430px) {
  .cfg-opts { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .5rem; }
  .cfg-opt { padding: .7rem .65rem; gap: .15rem .45rem; }
}

/* ---- Header at phone widths (QC 26 Sep: the burger ended at 384px in a
   375px viewport). stark.css makes the header a 52px | 1fr | 52px grid, but
   the 240px brand is wider than the 1fr track at 375, so the tracks overflow
   the container, and store.css adds a 14px right margin to the burger on
   top. Fixed here, scoped to .shop, stark.css and store.css untouched:
   the middle track may shrink (the brand stays centred over it), and the
   burger sits 5px in from the container's right edge, mirroring the basket
   icon's 5px on the left, so both have the same margin to the screen edge. */
@media (max-width: 1080px) {
  .shop .snav__inner { grid-template-columns: 52px minmax(0, 1fr) 52px; }
  .shop .snav__inner > .nav { justify-self: end; margin-right: 5px; }
  .shop .snav__toggle { margin-right: 0; }
  /* C5: the burger is a 44px touch target (it was 39x30). */
  .shop .snav__toggle { min-width: 44px; min-height: 44px; justify-content: center; align-items: center; }
}


/* =========================================================
   Slice 3 (brief Y, 29 Sep 2026): the light shop front.
   Laurence's rulings, verbatim from card-variants/light.html:
   "lighten things up. Chris is also tired of dark background colours".
   The synced brand files (stark.css, store.css, shop-prototype.css) are
   untouched; everything here is new. Colours are the stark tokens plus the
   three ground hexes #ffffff, #eef6fc and #dcecf8.
   ========================================================= */
:root {
  /* tweakable */
  --pc-radius: 28px; --pc-gap: 1.4rem;
  --pc-ground: 1; --pc-pill: 1rem;
  /* Re-skin 23 Oct 2026 (spec B6). --pc-shadow was a unitless alpha (.10) fed into rgba(); it is now the whole
     shadow, so every use below is rewritten. Offset plus blur only, no zero-offset halo. */
  --pc-shadow: 0 18px 40px -18px rgba(4,18,51,.55), 0 2px 0 rgba(255,255,255,.35) inset;
  --pc-shadow-hi: 0 24px 46px -18px rgba(4,18,51,.62), 0 2px 0 rgba(255,255,255,.35) inset;
  /* Spec 7: secondary text on a light card is Navy at 80% (6.9:1 on white), not grey. Re-declares
     --shop-ink-soft from css/shop-prototype.css:29 (#3d5878). */
  --shop-ink-soft: rgba(0,49,121,.8);
  /* Tabler "circle-check" (filled), inline from _tools/icons/tabler/icons/filled/circle-check.svg, used as a mask for the selection tick. */
  --tick: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M17 3.34a10 10 0 1 1 -14.995 8.984l-.005 -.324l.005 -.324a10 10 0 0 1 14.995 -8.336zm-1.293 5.953a1 1 0 0 0 -1.32 -.083l-.094 .083l-3.293 3.292l-1.293 -1.292l-.094 -.083a1 1 0 0 0 -1.403 1.403l.083 .094l2 2l.094 .083a1 1 0 0 0 1.226 0l.094 -.083l4 -4l.083 -.094a1 1 0 0 0 -.083 -1.32z'/%3E%3C/svg%3E");
  --sky-wash: color-mix(in srgb, #C5DCF7 55%, #fff);   /* Navy on it is 10.2:1 */
  --cfg-bar-h: 124px;   /* the phone price bar, collapsed: 12px + 44px toggle + 54px button row + 12px, 114 to 123px measured by the 27 Sep rules */
}

/* ---- page ground (re-skin 23 Oct 2026, spec A1): the BRIGHT blue radial from DESIGN.md section 3 / brand rule 2.
   It sits on <html> (not the body) at scroll attachment, so it is sized to the whole document: no one-viewport
   seam, no iOS fixed-attachment repaint, and it flows straight into the dark Abyss footer. The body is clear. ---- */
html {
  min-height: 100%;
  background-color: #0A4187;
  background-image: radial-gradient(120% 95% at 50% 42%, #1BA8E6 0%, #0B8BD1 22%, #0A6FBF 45%, #0A55A3 70%, #0A4187 100%);
  background-attachment: scroll;
}
body.shop {
  color: var(--ink);
  background: transparent;
  padding-bottom: 0;          /* stark.css pads the body 76px at 680px and under for a tab bar this app does not draw */
}

/* ---- section: light, no dark ground ---- */
.ssec.lt-sec { background: none; padding: clamp(2rem, 4.5vw, 3.5rem) 0 clamp(3rem, 6vw, 5rem); color: var(--ink); overflow: visible; }
.lt-sec .ssec__title { color: var(--navy); }

/* ---- the intro block on the front ---- */
.front-intro { margin: 0 0 clamp(1.4rem, 3vw, 2rem); max-width: 60rem; }
/* Re-skin 23 Oct (spec 3, 11): on-ground display text is white, at 3:1+ sizes only (24px and up, or 18.66px bold). */
.front-title { margin: 0 0 .6rem; font-family: var(--font-d); font-weight: 700; text-transform: uppercase; color: #fff; font-size: clamp(2.6rem, 6vw, 4.6rem); line-height: .98; text-wrap: balance; }
.front-lead { margin: 0; color: #fff; font-weight: 700; font-size: clamp(1.2rem, 1.9vw, 1.45rem); line-height: 1.4; max-width: 36rem; text-wrap: balance; }

/* ---- the drop zone card: an instant price from the front page ---- */
.dropcard { margin: 0 0 .9rem; padding: 0; background: none; border: 0; box-shadow: none; }
/* The zone IS the card: the settled card's white, 1px edge, 28px radius and .10 shadow. The dashed edge
   only shows while a file is dragged over it. */
.dropcard__zone {
  position: relative; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 1rem 1.3rem;
  padding: clamp(1rem, 2.4vw, 1.6rem); cursor: pointer; min-height: 44px;
  background: #fff; border: 1px solid rgba(0,49,121,.12); border-radius: var(--pc-radius);
  box-shadow: var(--pc-shadow);
  transition: border-color .18s ease, box-shadow .18s ease;
}
.dropcard__zone:hover { box-shadow: var(--pc-shadow-hi); }
.dropcard__zone.is-over { border-style: dashed; border-color: var(--navy); box-shadow: inset 0 0 0 1px var(--navy), var(--pc-shadow); }
.dropcard__zone:has(.dropcard__input:focus-visible) { outline: 3px solid #fff; outline-offset: 2px; box-shadow: 0 0 0 5px #003179, var(--pc-shadow); }   /* spec 4 */
.dropcard__zone.has-file { border-color: var(--red); }
.dropcard__input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
.dropcard__icon { width: 64px; height: 64px; border-radius: 50%; display: grid; place-items: center; background: var(--navy); color: #fff; }
.dropcard__icon svg { width: 30px; height: 30px; }
.dropcard__text { display: grid; gap: .25rem; min-width: 0; }
.dropcard__text b { font-family: var(--font-d); font-weight: 700; text-transform: uppercase; color: var(--navy); font-size: clamp(1.5rem, 3vw, 2.1rem); line-height: 1.05; text-wrap: balance; }
.dropcard__text span { color: var(--shop-ink-soft); font-size: .95rem; line-height: 1.45; }
.dropcard__btn {
  display: inline-flex; align-items: center; justify-content: center; min-height: 44px; padding: 0 1.4em; border-radius: 999px; color: #fff;
  font-weight: 800; font-size: 1rem; text-transform: uppercase; letter-spacing: .08em; white-space: nowrap;
  background: linear-gradient(120deg, var(--red) 80%, var(--red-hot));
  box-shadow: 0 8px 22px -10px rgba(213,35,47,.75), inset 0 1px 0 rgba(255,255,255,.25);
}
.dropcard__read { margin: .8rem 0 0; }
.dropcard__read:empty { display: none; }
.dropcard__alt { margin: 0 0 clamp(1.2rem, 3vw, 1.8rem); font-size: .95rem; font-weight: 600; }
/* Spec 2: small text that sits straight on the ground rides on a glass chip (white on it is 6.4:1 over the cyan core,
   more everywhere else). One shared recipe, applied to each on-ground small-text element below. */
.dropcard__alt, .dropcard__read:not(:empty) {   /* the step shell's own chips (.wz-back, .wz-count, .wz-bar) are in order.css */
  background: rgba(4,18,51,.45); -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  color: #fff; border-radius: 999px;
}
.dropcard__alt { display: inline-block; padding: .35rem .8rem; }
.dropcard__read:not(:empty) { display: inline-block; padding: .35rem .8rem; color: #fff; }
.dropcard__read[data-state="error"] { color: #FFC510; }
.dropcard__read[data-state="busy"] { color: #fff; }
@media (max-width: 640px) {
  .dropcard__zone { grid-template-columns: auto minmax(0, 1fr); }
  .dropcard__btn { grid-column: 1 / -1; }
  .dropcard__icon { width: 52px; height: 52px; }
}

/* ---- the grid ---- */
.pc-grid { display: grid; gap: var(--pc-gap); grid-template-columns: repeat(3, minmax(0, 1fr)); padding-bottom: 1rem; }   /* the 1rem keeps the gap between two groups at least twice the gap between two cards */
@media (max-width: 1080px) { .pc-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px) { .pc-grid { grid-template-columns: minmax(0, 1fr); } }
/* Re-skin 23 Oct (spec 8): index.php stamps data-n with the number of cards in the group, so a short row fills the width.
   Below 900px every group keeps the grid above (one column on a phone, two on a tablet). */
@media (min-width: 641px) { .pc-grid[data-n="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 901px) {
  .pc-grid[data-n="1"] { grid-template-columns: minmax(0, 1fr); }
  .pc-grid[data-n="1"] .pc { display: grid; grid-template-columns: 40% minmax(0, 1fr); min-height: 260px; }
  .pc-grid[data-n="1"] .pc__photo { position: relative; aspect-ratio: auto; height: 100%; border-radius: var(--pc-radius) 0 0 var(--pc-radius); }
  /* the picture is taken out of flow so the body, not the image's own height, sets the card height (min-height 260px holds) */
  .pc-grid[data-n="1"] .pc__photo img { position: absolute; inset: 18px; width: calc(100% - 36px); height: calc(100% - 36px); object-fit: contain; }
  .pc-grid[data-n="1"] .pc__body { justify-content: center; padding: 1.4rem 1.6rem; }
  .pc-grid[data-n="1"] .pc__foot { margin-top: 0; padding-top: .9rem; max-width: 26rem; }
  .pc-grid[data-n="2"] .pc__photo { aspect-ratio: 3 / 2; }
}

/* ---- card: C's shape on white ---- */
.pc {
  --pc-grey: rgba(0,49,121,.8);   /* spec 7: was #5A6472 */
  display: flex; flex-direction: column; position: relative; isolation: isolate; overflow: hidden;
  min-height: 44px; text-decoration: none; cursor: pointer; color: var(--navy);
  background: #fff; border: 1px solid rgba(0,49,121,.12); border-radius: var(--pc-radius);
  box-shadow: var(--pc-shadow);
  transition: translate .2s ease, box-shadow .2s ease;
}
.pc:hover, .pc:focus-visible { translate: 0 -2px; box-shadow: var(--pc-shadow-hi); }
.pc:hover { color: var(--navy); }
.pc:focus-visible { outline: 3px solid #fff; outline-offset: 2px; }
/* spec 4: white 3px ring with a 2px Navy inner ring, readable on the bright ground and on the card edge. Beats .shop a:focus-visible
   (css/shop-prototype.css:40), which also squares the card's radius to 8px, so the radius is held here. */
.shop .pc:focus-visible { outline: 3px solid #fff; outline-offset: 2px; border-radius: var(--pc-radius); box-shadow: 0 0 0 5px #003179, var(--pc-shadow-hi); }
@media (prefers-reduced-motion: reduce) { .pc { transition: none; } .pc:hover, .pc:focus-visible { translate: none; } }

/* Spec 7: the photo well is tinted from the palette (white into Sky #C5DCF7), padding 18px, with a soft contact shadow under the object.
   The product photos are opaque JPGs on a pale studio backdrop, so multiply lets that backdrop take the well's tint instead of
   showing as a grey box, and lets the shadow below read through it. */
.pc__photo { position: relative; aspect-ratio: 4 / 3; overflow: hidden; background: radial-gradient(#fff 0%, #C5DCF7 100%); padding: 18px; box-sizing: border-box; border-radius: var(--pc-radius) var(--pc-radius) 0 0; }
.pc__photo::after { content: ""; position: absolute; left: 24%; right: 24%; bottom: 12px; height: 14px; border-radius: 50%; background: radial-gradient(rgba(4,18,51,.32), rgba(4,18,51,0) 70%); filter: blur(4px); pointer-events: none; }
.pc__photo img { position: relative; z-index: 1; width: 100%; height: 100%; object-fit: contain; display: block; mix-blend-mode: multiply; }
.pc__plate { display: grid; place-items: center; width: 100%; height: 100%; border-radius: 18px; background: #C5DCF7; color: var(--navy); font-family: var(--font-d); font-weight: 700; text-transform: uppercase; font-size: 1.8rem; text-align: center; padding: 1rem; }
.pc__body { display: flex; flex-direction: column; gap: .35rem; flex: 1; padding: 1rem 1.1rem 1.1rem; }
.pc__body h3 {
  margin: 0; color: var(--navy); font-family: var(--font-d); font-weight: 700; text-transform: uppercase;
  font-size: clamp(1.4rem, 1.9vw, 1.65rem); line-height: 1.15; overflow-wrap: anywhere;
}
/* Spec 9: the client's blurb shows in full (three lines; it was cut mid-word on one). The foot is pushed to the bottom so the pills line up. */
.pc__blurb {
  margin: 0; color: var(--pc-grey); font-size: .86rem; line-height: 1.5; min-height: calc(3 * 1.5em);
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; white-space: normal;
}
/* Spec 10: the foot is always two rows, on all cards: the price line, then the ORDER NOW pill full width. */
.pc__foot { margin-top: auto; padding-top: .55rem; display: flex; flex-direction: column; align-items: stretch; gap: .6rem; }
.pc__price { color: var(--pc-grey); font-size: .8125rem; line-height: 1.25; white-space: nowrap; font-variant-numeric: tabular-nums; }
.pc__price b { color: var(--navy); font-size: 1.1rem; font-weight: 800; }

/* the one pill: red held to 80% so the label never sits on the pale end (white on --red-hot alone is 3.3:1) */
.pc__go {
  width: 100%;
  display: inline-flex; align-items: center; justify-content: center; gap: .5em; min-height: 44px;
  padding: 0 1.2em; border-radius: 999px; color: #fff;
  font-family: var(--font-b); font-weight: 800; font-size: var(--pc-pill); text-transform: uppercase; letter-spacing: .08em; line-height: 1;
  background: linear-gradient(120deg, var(--red) 80%, var(--red-hot));
  box-shadow: 0 8px 22px -10px rgba(213,35,47,.75), inset 0 1px 0 rgba(255,255,255,.25);
}
.pc__arrow { flex: none; width: 1.1em; height: 1.1em; transition: translate .2s ease; }
.pc:hover .pc__arrow { translate: 3px 0; }
@media (prefers-reduced-motion: reduce) { .pc:hover .pc__arrow { translate: none; } .pc__arrow { transition: none; } }

/* small shared bits the order pages and the basket use */
.linkbtn {
  display: inline-flex; align-items: center; gap: .4rem; min-height: 44px; padding: 0 .2rem; border: 0; background: none; cursor: pointer;
  font: inherit; font-size: .9rem; font-weight: 800; color: var(--navy); text-decoration: underline; text-underline-offset: 3px;
}
.linkbtn:hover { color: var(--red); }
.linkbtn svg { width: 1.25em; height: 1.25em; flex: none; }
.field__err { display: block; margin-top: .3rem; font-size: .82rem; font-weight: 700; color: var(--red); }

/* Brief AB: the basket icon in the phone header is a tap target, so 44px (stark.css draws it at 42px). */
@media (max-width: 780px) { .snav__inner .snav__mini { width: 44px; height: 44px; } }
/* Brief AB: the footer links are tap targets on a phone, so 44px tall (stark.css leaves them at 20px). */
@media (max-width: 780px) {
  .sfoot ul { gap: 0; }
  .sfoot ul a { display: inline-flex; align-items: center; min-height: 44px; }
}

/* Main nav now carries the home page's eight items, so on a wide screen the basket shrinks to its icon and count
   (the word stays for screen readers and for the phone menu). */
@media (min-width: 1081px) {
  .shop .snav__cart .snav__cart-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .shop .snav__menu a { padding-inline: clamp(.2rem, .3vw, .6rem); }
  .shop .snav__menu a.snav__cta { padding-inline: clamp(.6rem, .8vw, 1.1rem) !important; }
}

/* Long shop fronts (a store with dozens of products): category headings and jump links.
   A short list never prints these. Re-skin 23 Oct (spec 11, 12): the pills are glass chips on the ground (white 14% fill over the
   deep-blue glass, 1px white 40% border, white text 14px); the current one, and hover or focus, is Yellow with Navy text (7.7:1). */
.pc-jump { display: flex; flex-wrap: wrap; gap: .5rem; margin: 0 0 1.4rem; }
.pc-jump a {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 1rem; border-radius: 999px;
  /* white 14% over deep-blue glass: bare white 14% on the cyan core gives white text 2.4:1 (builder measured 23 Oct), the glass layer lifts it past 6:1 */
  background: linear-gradient(rgba(255,255,255,.14), rgba(255,255,255,.14)), rgba(4,18,51,.45);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  border: 1px solid rgba(255,255,255,.4); color: #fff; font-weight: 600; font-size: .875rem; text-decoration: none;
}
.pc-jump a:hover, .pc-jump a:focus-visible, .pc-jump a[aria-current], .pc-jump a.is-on { background: #FFC510; border-color: #FFC510; color: #003179; }
/* The chip for the group the visitor just jumped to. :target is the only live "where am I" a static page has. */
main:has(#grp-1:target) .pc-jump a[href="#grp-1"], main:has(#grp-2:target) .pc-jump a[href="#grp-2"], main:has(#grp-3:target) .pc-jump a[href="#grp-3"],
main:has(#grp-4:target) .pc-jump a[href="#grp-4"], main:has(#grp-5:target) .pc-jump a[href="#grp-5"], main:has(#grp-6:target) .pc-jump a[href="#grp-6"],
main:has(#grp-7:target) .pc-jump a[href="#grp-7"], main:has(#grp-8:target) .pc-jump a[href="#grp-8"], main:has(#grp-9:target) .pc-jump a[href="#grp-9"],
main:has(#grp-10:target) .pc-jump a[href="#grp-10"], main:has(#grp-11:target) .pc-jump a[href="#grp-11"], main:has(#grp-12:target) .pc-jump a[href="#grp-12"] {
  background: #FFC510; border-color: #FFC510; color: #003179;
}
/* Spec 11: group heading is display size, white, with a short Yellow bar under it (a rule, not a coloured border-left). */
.pc-group { margin: 2.2rem 0 1rem; font-family: var(--font-d); font-weight: 700; font-size: clamp(2rem, 3.4vw, 2.6rem); line-height: 1.05; color: #fff; text-wrap: balance; scroll-margin-top: 90px; }
.pc-group::after { content: ""; display: block; width: 3.5rem; height: 4px; margin-top: .5rem; border-radius: 2px; background: #FFC510; }
.pc-group:first-of-type { margin-top: 1rem; }


/* =========================================================
   RE-SKIN 23 Oct 2026 (Steve's print product system, SPEC r1 sections A, B, E, G).
   Direction is the ruled bright-blue ground (DESIGN.md section 3, brand rule 2). Every rule below re-declares something in the
   synced css/stark.css, css/store.css or css/shop-prototype.css, which are untouched; the origin line is named on each.
   ========================================================= */

/* ---- Spec 16: choice tiles, shop.css half (.mopt and .cfg-opt; .wz-tile, .wz-chip and .ph-chip are in order.css) ----
   Re-declares css/shop-prototype.css:450-461. Unselected border is Navy at 55% (3.35:1 on white; .5 measures 2.94:1, under the 3:1 line).
   Selected = Sky wash + 2px Red edge + a Tabler circle-check badge top right, so colour alone never marks the choice. */
.mopt, .cfg-opt { position: relative; background: #fff; border-color: rgba(0,49,121,.55); }
.mopt:hover, .cfg-opt:hover { border-color: rgba(0,174,239,.9); background: #fff; }
.mopt:has(input:checked), .cfg-opt:has(input:checked) {
  border-color: #D5232F; background: var(--sky-wash);
  box-shadow: 0 0 0 1px #D5232F, 0 14px 30px -18px rgba(213,35,47,.6);
}
.mopt:has(input:checked)::after, .cfg-opt:has(input:checked)::after {
  content: ""; position: absolute; top: -6px; right: -6px; width: 20px; height: 20px; background: #D5232F;
  -webkit-mask: var(--tick) center / contain no-repeat; mask: var(--tick) center / contain no-repeat; pointer-events: none;
}
.mopt:has(input:focus-visible), .cfg-opt:has(input:focus-visible) { outline: 3px solid #fff; outline-offset: 2px; box-shadow: 0 0 0 5px #003179; }
.cfg-opt:has(input:checked) .mopt__price { color: var(--navy); }   /* Red on the wash is 4.26:1, under 4.5 for 13px text */

/* ---- Spec 4: one focus ring for the ground and the card edges: white 3px with a 2px Navy inner ring.
   Re-declares css/shop-prototype.css:40-49 (a 2px cyan ring, 2.5:1 on white and lost on the cyan core). ---- */
.shop a:focus-visible, .shop button:focus-visible, .shop summary:focus-visible, .shop [tabindex]:focus-visible,
.shop input:focus-visible, .shop select:focus-visible, .shop textarea:focus-visible {
  outline: 3px solid #fff; outline-offset: 2px; box-shadow: 0 0 0 5px #003179;
}
/* The stepper pill clips anything outside it, so its ring stays inside, in Navy (white would vanish on the white pill). */
.shop .qty :focus-visible { outline-color: #003179; outline-offset: -3px; box-shadow: none; }

/* ---- Spec 18: the 13px floor reaches the synced footer and nav labels too (css/stark.css .sfoot h4 10.6px, .sfoot__bar 12.5px;
   css/shop-prototype.css .snav__cart b 11.5px) ---- */
.shop .sfoot h4, .shop .sfoot__bar, .shop .sfoot__bar p, .shop .sfoot__franchise, .shop .snav__cart b { font-size: .8125rem; }

/* ---- Spec 24: tabular figures on every rand amount ---- */
.pc__price, .pc__price b, .osum__total b, .osum__lines b, .cfg-sum [data-total], .cfg-sum__total b, .cfg-sum__printing b, .cfg-sum__extras b,
.cfg-sum__percopy b, .wz-working b, .mopt__price, .cfg-opt .mopt__price, .ph-linetotal, .ph-check b, .wz-extra b, .psum__price, .cfg-bline__end,
.cfg-ful__lines, .ph-line, .ph-chip small, .wz-chip em, .pr-pick small, [data-price-for], [data-per-copy] { font-variant-numeric: tabular-nums; }

/* ---- Spec 24: selection, caret, scrollbar ---- */
::selection { background: #FFC510; color: #003179; }
input, textarea, select, [contenteditable] { caret-color: #D5232F; }
html { scrollbar-color: rgba(255,255,255,.6) rgba(4,18,51,.35); }
textarea, .cfg-sum { scrollbar-color: rgba(0,49,121,.45) transparent; }

/* ---- Spec 20: the phone price bar. The 124px used to pad the BODY (a pale strip under the footer); it now clears the bar where it
   matters: under the last question, under the footer's copyright line, and under the feedback pill (which used to sit on the
   bar's "See how it adds up" toggle). Re-declares the old rule at the phone block above and css/stark.css:936. ---- */
@media (max-width: 780px) {
  .ord-sec:has(.cfg-sum) { padding-bottom: calc(var(--cfg-bar-h) + 8px + env(safe-area-inset-bottom)); }
  body:has(.cfg-sum) .sfoot { padding-bottom: calc(2rem + var(--cfg-bar-h) + env(safe-area-inset-bottom)); }
  body:has(.cfg-sum) .fbk-btn { bottom: calc(env(safe-area-inset-bottom) + var(--cfg-bar-h) + 8px); }   /* feedback.js (preview only) sets 84px */
}
