/* Gift card product page (6666) — rebuild
   See docs/gift-card-product-baseline.md and functions/giftCardProductLayout.php.

   Everything here is scoped to .postid-6666 so no other product can be affected.

   ── On the numbers ────────────────────────────────────────────────────────────
   These are now taken from the FIGMA EXPORT for design image 1 (2026-09-09),
   replacing the values previously measured off the screenshots.

   SCALE FACTOR IS 1.0 — the values below are authored, not scaled, so they are
   used literally with no --scale multiplier. Two independent checks:

     - The "Wayvo Gift Card" text style definition reads 40px / 110% / -2%, and
       the layer in the export reads font-size: 40px / line-height: 110% /
       letter-spacing: -0.02em. A scaled instance would disagree; these match.
     - Every geometry number is a clean integer (1440 / 1360 / 900 / 440 / 360 /
       120 / 40 / 20 / 16 / 6). None of the long decimals that betray a scaled
       instance appear anywhere in the layout — the only decimals in the whole
       paste (414.84, 260.09, 51.7, 25.0835) are inside the card ARTWORK, which
       is an image composition rather than layout.

   The screenshot measurements turned out to be good: the card width was read as
   415 against an actual 414.84, and the panel padding, title size, body size and
   both swatch numbers were already exact. The misses were the column gap (read
   as 40, actually 20), the corner radius (16 vs 8), the button height (52 vs 46)
   and the small text size (14 vs 12).
   ────────────────────────────────────────────────────────────────────────────── */

.postid-6666 {
	/* ── Colour ──────────────────────────────────────────────────────────── */
	--gcp-ground: #f5f5f5;
	--gcp-surface: #ffffff;
	--gcp-ink: #101011;
	--gcp-ink-soft: #8d8b8b;
	/* Rules are the same grey as the muted text, drawn at a half pixel. */
	--gcp-line: #8d8b8b;
	--gcp-line-width: 0.5px;
	/* Buttons that are not the primary action sit on the page ground colour
	   rather than on white, even though the panel behind them is white. */
	--gcp-control-bg: #f5f5f5;

	/* ── Type ────────────────────────────────────────────────────────────── */
	/* The export reports Geist for content and Inter for the three controls.
	   Confirmed with Paul that the Inter is a mistake in the file — Geist is the
	   only typeface, controls included — so there is deliberately ONE font token
	   here rather than a separate one for the UI.

	   Geist is enqueued for this page in functions.php. The fallbacks matter: if
	   that request ever fails, system-ui is a closer match to Geist's proportions
	   than OceanWP's default would be. */
	--gcp-font: 'Geist', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

	/* DELIBERATE DIVERGENCE FROM THE EXPORT, set by Paul 2026-09-11.
	   The Figma text style and the PNGs both say 40px: the title-to-price
	   baseline distance measures 88px at 2x = 44 @1x, which is 40 x 110%. It was
	   rendering correctly at 40 and still read too large in the browser, so this
	   is a judgement call about the real page, not a conversion error.
	   Drives the title AND the price, which the design keeps the same size. */
	--gcp-title-size: 30px;   /* H3 — export says 40; see above */
	--gcp-title-lh: 1.1;
	--gcp-title-weight: 500;
	--gcp-body-size: 16px;    /* P4 */
	--gcp-body-lh: 1.4;
	--gcp-small-size: 12px;   /* P5 */
	--gcp-small-lh: 1.3;
	/* Two different values in the export: body/description is -0.01em, headings
	   and the small print are -0.02em. */
	--gcp-track-tight: -0.02em;
	--gcp-track-body: -0.01em;

	/* ── Frame ───────────────────────────────────────────────────────────── */
	/* 1440 frame, 40px side padding, so the content row is 1360 = 900 + 20 + 440. */
	--gcp-max-width: 1360px;
	/* 72 = the export's 120 less 40%, asked for 2026-09-11 once the 120 was
	   actually rendering (it had been losing to OceanWP's 50 and nobody had seen
	   it at full size). This is also what pulls the sticky card into its resting
	   position at load — see --gcp-sticky-top. */
	--gcp-section-pad-top: 72px;
	--gcp-section-pad-x: 40px;
	--gcp-section-pad-bottom: 40px;
	--gcp-col-gap: 20px;

	/* Left column. The export draws it as a 900x760 rounded surface, but Paul
	   confirmed its fill is #F5F5F5 — the SAME as the section behind it. So it is
	   invisible by construction: the 8px radius never shows, and there is no
	   surface to paint. It is a layout box, not a card, which is why nothing here
	   is applied yet. Phase 5/7 will use these to distribute the breadcrumb, the
	   artwork and the footer mark down its height with space-between. */
	--gcp-stage-width: 900px;
	--gcp-stage-height: 760px;
	--gcp-stage-pad-y: 40px;
	--gcp-stage-pad-x: 20px;

	/* Right panel. 440 outer, 40 padding, so every child inside is 360 wide —
	   which is what the export shows on all of them. */
	--gcp-panel-width: 440px;
	--gcp-panel-pad: 40px;
	--gcp-panel-height: 760px;
	/* Gap between the panel's three top-level blocks. */
	--gcp-panel-gap: 40px;
	/* Gap inside the lower block: options, rules, controls, add to basket. */
	--gcp-block-gap: 20px;
	/* Title block to description. */
	--gcp-title-gap: 16px;

	--gcp-radius: 8px;

	/* ── Controls ────────────────────────────────────────────────────────── */
	--gcp-button-height: 46px;
	--gcp-button-pad-x: 20px;
	/* Pill, not a rounded rectangle — the export says 9999px on all three. */
	--gcp-button-radius: 9999px;
	/* No token for the 170px control width in the export's Frame 731: that row
	   holds the value dropdown AND a quantity stepper, and the stepper is not
	   being built (see the quantity rule further down). The value control takes
	   the full 360 on its own, so it needs no width of its own. */

	/* Card artwork: 414.84 x 260.09 in the export. Rounded to 415 for the width;
	   the ratio is kept at full precision because it is what pins the box height. */
	--gcp-card-width: 415px;
	--gcp-card-aspect: 1.595;

	/* 414.84 / 1.595 = 260.09, which is what the export gives for the artwork's
	   height. Stated rather than derived because the sticky offset below needs it
	   as a length. */
	--gcp-card-height: 260px;

	/* Where the sticky card comes to rest (phase 7): the vertical centre of the
	   viewport. Half the viewport height, less half the card, puts the card's own
	   middle on the viewport's middle at any window size.

	   This deliberately does NOT try to clear the fixed site header. An earlier
	   version pinned the card just below it, which is not the behaviour asked for
	   — and the header's own --wayvo-inset / --wayvo-height are declared on
	   .wayvo-header rather than :root, so they are not in scope here anyway, and
	   are redefined per breakpoint on top of that. Centring sidesteps the whole
	   question: the middle of the viewport is well clear of a header at the top.

	   WHY THE CARD USED TO HOP UP ON THE FIRST SCROLL. Sticky only engages once
	   the element's natural position would rise above this offset. If the card's
	   at-rest position on load is BELOW it, sticky is dormant and the card rides
	   up with the page until it catches — a small jump, then it pins. If the
	   at-rest position is ABOVE it, sticky pushes the card DOWN to exactly this
	   offset at scroll 0 and it never moves. So "correct on load" is the
	   condition

	       header + section-pad-top + stage-pad-y + breadcrumb  <=  50vh - 130

	   and cutting section-pad-top from 120 to 72 is what brings the left side of
	   that under the right on an ordinary window. It is viewport-HEIGHT
	   dependent: a short window lowers the right-hand side and the hop can come
	   back. If it does, the honest lever is less padding above, not a smaller
	   offset, because the offset is what centres the card. */
	--gcp-sticky-top: calc(50vh - (var(--gcp-card-height) / 2));

	/* REMOVED: --gcp-stage-tail. Kept as a note because the reasoning that
	   produced it was backwards and is easy to reproduce.

	   A sticky element is released at the BOTTOM of its containing block, and
	   that release point is also where it comes to rest. Extending the stage
	   downward to "buy more travel" therefore moves the card's final position
	   further DOWN — so a 40vh tail left the card hanging in empty space well
	   below the end of the panel, which is the opposite of the ask. More travel
	   is not more stickiness; it is a longer fall.

	   The behaviour actually wanted is plain sticky with the containing block
	   ending where the section ends: hold the viewport centre, then move up with
	   the page at page speed, never independently. That needs no extra height at
	   all — see the padding note on .wayvo-gcp-stage. */

	/* The gap kept BELOW the card when it finally comes to rest, so it never ends
	   up hugging the bottom of the panel.

	   This is the same number as --gcp-sticky-top, and that is the whole trick,
	   not a coincidence. The card unpins when its containing block's bottom
	   reaches (sticky-top + card-height). Make the space below the card equal the
	   space the sticky offset leaves above it and the two cancel: the card unpins
	   exactly as the panel's bottom edge arrives at the BOTTOM OF THE VIEWPORT,
	   still sitting dead centre with its gap intact. Before that moment it is
	   pinned; after it, it travels with the page. There is no in-between where it
	   slides down the column on its own.

	   min() caps it. On a very tall window half the viewport can exceed the room
	   the stage actually has in the collapsed state (short panel, before an amount
	   is chosen), and an over-large padding leaves no travel at all — the card
	   stops centring and sits under the breadcrumb instead. 320 is the gap at a
	   900px window, which is where this was tuned. */
	--gcp-card-rest-gap: min(var(--gcp-sticky-top), 320px);

	/* Where the breadcrumb pins. Unlike the card, this one DOES have to clear the
	   fixed site header, because it rides near the top of the viewport rather
	   than through the middle.

	   20 inset + ~48 bar + 20 breathing room = 88. Derived by hand rather than
	   referenced, because .wayvo-header declares --wayvo-inset and --wayvo-height
	   on ITSELF, not on :root, so they are out of scope here — and it restates
	   them per breakpoint on top of that. RE-DERIVE THIS IF THE HEADER CHANGES
	   HEIGHT; nothing will fail loudly, the breadcrumb will just tuck under the
	   bar. (The header also sits lower while the top banner is on screen, but the
	   banner has scrolled away long before the breadcrumb reaches its offset.) */
	--gcp-breadcrumb-sticky-top: 88px;

	/* 15 swatches at 16px with 6px between them span 324 of the panel's 360 inner
	   width. Both numbers were already correct; the count is now confirmed at 15. */
	--gcp-swatch-size: 16px;
	--gcp-swatch-gap: 6px;
}

/* ── Phase 1 carry-over ──────────────────────────────────────────────────── */

/* display: contents makes these generate no box of their own while their
   children keep participating in the parent's layout. #pwgc-purchase-container
   is Pimwick's markup styled by Pimwick's CSS, which is not in this repo, so
   this is what guarantees that introducing the panel wrapper cannot regroup the
   container's children whatever that CSS does.

   Still needed: phase 2 restyles the OUTER shell, and the panel's own contents
   are not rearranged until phases 3-5. It comes off when they are. */

#wayvo-gc-panel,
#wayvo-gc-picker-slot {
	display: contents;
}

/* ── Page ground ─────────────────────────────────────────────────────────── */

/* Painted on both: OceanWP sets white on one or the other depending on the
   layout setting, and only one of them is the element actually seen. */
.postid-6666,
.postid-6666 #main {
	background: var(--gcp-ground);
}

/* The design runs much wider than OceanWP's default container: a 1440 frame with
   40px of side padding gives a 1360 content row, not a 1200 column.

   1360 is the CONTENT row, so with border-box the outer max-width has to carry
   the 40px of side padding on top of it — 1360 + 40 + 40 = the 1440 frame. */
.postid-6666 #content-wrap.container {
	max-width: calc(var(--gcp-max-width) + (var(--gcp-section-pad-x) * 2));
	padding-left: var(--gcp-section-pad-x);
	padding-right: var(--gcp-section-pad-x);
}

/* The export puts 120px above the content and 40 below it. OceanWP's own page
   spacing is whatever the customizer says, so this is stated rather than
   inherited.

   #main has to be in the selector. OceanWP's style.css carries

       #main #content-wrap { padding-top: 50px; padding-bottom: 50px }   (2,0,0)

   and TWO IDs beat one ID plus a class, so `.postid-6666 #content-wrap` at
   (1,1,0) lost and the page was rendering OceanWP's 50/50 rather than the
   export's 120/40. Found by the specificity audit, not by eye — 50 against 120
   is a big miss that still looks plausible on screen, which is exactly why it
   survived. body.postid-6666 #main #content-wrap is (2,1,1). */
body.postid-6666 #main #content-wrap {
	padding-top: var(--gcp-section-pad-top);
	padding-bottom: var(--gcp-section-pad-bottom);
}

/* OceanWP draws a card/border around post content on some settings; the panel
   below is the only surface this page should have. */
.postid-6666 #content .entry-content,
.postid-6666 #primary,
.postid-6666 #content {
	background: none;
	border: 0;
	box-shadow: none;
	padding: 0;
}

/* Defensive, and load-bearing for the sticky card below.

   position:sticky is cancelled by ANY ancestor with overflow other than visible,
   silently — no error, and devtools shows the rule applied and simply not
   working. Auditing the theme's own stylesheets is not enough to rule it out:
   OceanWP writes customizer settings into an inline <style> block, and any plugin
   can add a rule too, neither of which is visible from the repo. Since this page
   is fully custom and nothing on it wants a clipped ancestor, stating it here
   removes a whole class of "it works locally" failure. */
.postid-6666 #main,
.postid-6666 #content-wrap,
.postid-6666 #primary,
.postid-6666 #content,
.postid-6666 #content .entry-content,
.postid-6666 div.product {
	overflow: visible;
}

/* ── Two columns ─────────────────────────────────────────────────────────── */

/* OceanWP floats the gallery and summary (hence its own clear_summary_floats
   helper). Grid on the parent makes those floats irrelevant, so they are
   neutralised on the children rather than fought with widths.

   TWO items in ONE row, not four items across two rows. The breadcrumb and the
   gallery live inside .wayvo-gcp-stage, which is what lets `stretch` give the left
   column exactly the panel's height — the thing the sticky card needs.

   NO grid-template-rows, deliberately. A second row was tried to give the sticky
   card travel past the end of the panel and made things worse, because a sticky
   element comes to rest at the bottom of its containing block: a taller stage
   just meant a longer fall into empty space. One row, both columns, nothing to
   reason about. */
.postid-6666 div.product {
	display: grid;
	grid-template-columns: minmax(0, 1fr) var(--gcp-panel-width);
	column-gap: var(--gcp-col-gap);
	/* Was `start`. Stretch is the whole mechanism: it is what makes the stage as
	   tall as the panel beside it. */
	align-items: stretch;
}

.postid-6666 div.product > * {
	float: none;
}

/* The left column. min-height keeps the collapsed state close to the export's
   760 stage; beyond that it takes its height from the panel. */
.postid-6666 .wayvo-gcp-stage {
	grid-column: 1;
	grid-row: 1;
	display: flex;
	flex-direction: column;
	min-height: var(--gcp-stage-height);
	/* THE BOTTOM PADDING IS THE CARD'S FLOOR. It is not spacing.

	   This box is the sticky card's containing block, so its bottom edge is where
	   the card stops being pinned AND where the card comes to rest. Whatever
	   padding sits here is therefore the gap left under the card at the end of
	   the travel — set it to 0 and the card slides right down until it is flush
	   with the bottom of the panel, which is exactly what the screenshot showed.

	   Two wrong turns got here, both worth not repeating:
	   - making the stage TALLER (a tail row) to buy more travel. A sticky element
	     rests at the bottom of its containing block, so a taller box is a longer
	     fall, not more grip. It left the card stranded below the panel.
	   - then removing this padding entirely, which is the same mistake in
	     miniature: it moved the floor DOWN to the panel's edge.
	   The floor has to come UP. That is this padding, and it wants to be large —
	   the 40px it originally carried was far too small to read as a gap.

	   Costs no page height: the stage is stretched to the panel's height by the
	   grid, so padding here eats into the content box rather than growing the
	   box. And the stage is the ground colour, so none of it is visible. */
	padding: var(--gcp-stage-pad-y) var(--gcp-stage-pad-x) var(--gcp-card-rest-gap);
}

/* Centred by constraining the gallery itself and letting auto margins do the
   work, rather than by making it a flex container. WooCommerce initialises
   flexslider on this element's inner wrapper even for a single image, and that
   sets its own width and transform - so the less we touch inside, the better.

   ── PHASE 7: the sticky card column ──────────────────────────────────────────
   The card holds the VERTICAL CENTRE OF THE VIEWPORT while the panel beside it
   scrolls, and lets go when the panel ends so the sections below scroll up
   normally.

   `top` is what produces the centring: half the viewport, less half the card, so
   the card's own middle lands on the viewport's middle. Nothing measures anything
   at runtime.

   Sticky also pushes an element DOWN when its natural position is closer to the
   top than the offset, which is why the card sits centred at rest rather than
   tucked under the breadcrumb — the same declaration covers both states.

   Two things it depends on, both of which the first attempt got wrong:

   1. THE PARENT MUST BE TALLER THAN THE CARD, or there is nothing to travel
      through and sticky silently does nothing. That is now .wayvo-gcp-stage,
      stretched by the grid to the panel's height. The first attempt relied on a
      `1fr` grid row growing to fit the panel, which the Grid spec does not
      promise: an item spanning a FLEXIBLE track is excluded from that track's
      intrinsic sizing, so the row stayed as short as the card.
   2. NO ANCESTOR MAY HAVE `overflow` OTHER THAN visible. One overflow:hidden
      anywhere up the tree turns sticky into a no-op with nothing in devtools to
      point at. Verified across the child theme and OceanWP; there is a test that
      scans for it, because it is the likeliest way this breaks later.

   No align-self here on purpose. As a flex item in a column, the card's natural
   position is under the breadcrumb, which is exactly where sticky wants to start
   measuring from.

   ── THE SELECTOR IS LONG ON PURPOSE. DO NOT SHORTEN IT. ──────────────────────
   Two other stylesheets set `position: relative` on this exact element:

     WooCommerce  .woocommerce div.product div.images.woocommerce-product-gallery
                  → 4 classes, 2 elements = (0,4,2)
     OceanWP      .woocommerce div.product .woocommerce-product-gallery
                  → 3 classes, 1 element  = (0,3,1)

   The obvious `.postid-6666 .wayvo-gcp-stage .woocommerce-product-gallery` is
   (0,3,0) and loses to both. That is exactly how this failed on the live site:
   `top` applied (nothing else sets it) while `position` stayed `relative`, so the
   rule looked present and correct in devtools and did nothing. A sticky element
   that is actually `position: relative` is just a normal element.

   This selector is 4 classes and 3 elements = (0,4,3), which clears WooCommerce's
   (0,4,2). Every part of it is load-bearing; dropping `div.product` or any `div`
   puts it back under one of the rules above. */
.postid-6666 div.product div.wayvo-gcp-stage div.woocommerce-product-gallery {
	width: 100%;
	max-width: var(--gcp-card-width);
	margin-inline: auto;
	position: sticky;
	top: var(--gcp-sticky-top);
}

/* On a very short viewport a centred sticky card would fill the screen and leave
   no room to fill the form in, so it goes back to scrolling normally. Keyed on
   height, not width: this is about vertical room, which a landscape phone runs
   out of first.

   THE NUMBER IS DERIVED, NOT GUESSED. The card is --gcp-card-height (260px) tall.
   At 480 it takes 54% of the screen, which is the point at which centring it
   stops leaving usable room either side. Above that it is a minority of the
   viewport and there is no reason to switch the behaviour off.

   It was 700, which was picked by feel and was far too aggressive: a 1014x663
   laptop window — an ordinary size, and exactly what you get with devtools docked
   along the bottom — fell under it, so the card sat there resolutely not sticking
   while every other measurement looked perfect. If this ever needs revisiting,
   reason from the card's height rather than from a round number. */
@media (max-height: 480px) {
	/* Same specificity arithmetic as the rule above — it has to outrank the same
	   two `position: relative` declarations to win. */
	.postid-6666 div.product div.wayvo-gcp-stage div.woocommerce-product-gallery {
		position: static;
	}
}

/* One row now. The panel used to span two so it started level with the
   breadcrumb; the breadcrumb is inside the stage rather than a grid item of its
   own, so a single row does the same job with nothing to reason about — and,
   crucially, without a flexible track whose sizing the sticky card depended on. */
.postid-6666 div.product .summary.entry-summary {
	grid-column: 2;
	grid-row: 1;
	width: auto;
	margin: 0;
	background: var(--gcp-surface);
	border-radius: var(--gcp-radius);
	padding: var(--gcp-panel-pad);
}

/* Everything after the summary — related products, upsells, the Elementor
   template — spans the full width underneath both columns. Without this, grid
   auto-placement drops each one into whichever cell happens to be free.

   The exclusion list is now just the two real columns. The breadcrumb and the
   gallery are no longer direct children of the grid, so naming them here would be
   dead weight — and worse, would read as though they still were. */
.postid-6666 div.product > *:not(.wayvo-gcp-stage):not(.summary) {
	grid-column: 1 / -1;
}

/* ── Card artwork ────────────────────────────────────────────────────────── */

/* The box is fixed by aspect ratio rather than by whatever the current image's
   intrinsic size happens to be, so hovering a swatch cannot change the page's
   height.

   That mattered more than it looks: the registry artwork has different
   dimensions from the product image, so previewing resized the card, which
   resized the page, which could add or remove the scrollbar — shifting the
   layout sideways and putting the cursor over the NEXT swatch. That reverted the
   artwork, shifted it back, and the two designs flickered against each other
   with the mouse completely still.

   object-fit: contain rather than cover: a design whose artwork is not quite the
   card ratio should letterbox, not be silently cropped. */
.postid-6666 .woocommerce-product-gallery img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: var(--gcp-card-aspect);
	object-fit: contain;
	border-radius: 12px;
}

/* The gallery's zoom/lightbox triggers and thumbnail strip are not in the
   design — a gift card has one image. */
.postid-6666 .woocommerce-product-gallery__trigger,
.postid-6666 .flex-control-thumbs {
	display: none;
}

/* ── Breadcrumb ──────────────────────────────────────────────────────────── */

/* In the export the breadcrumb sits at the top of the left stage with the card
   centred below it and space-between doing the spacing. That stage surface is
   not built yet, so an explicit gap stands in for it — replace with the stage's
   own distribution when phase 5 builds it. */
.postid-6666 .wayvo-gcp-breadcrumb {
	/* Sticky, like the card. It travels the whole stage — including the tail —
	   so the way back stays reachable for as long as the card is on screen.

	   This needs the same two conditions the card needs and gets them from the
	   same place: a parent taller than itself (the stage) and no clipping
	   ancestor (asserted above). It is a flex item, which sticky is fine with.

	   z-index because two sticky siblings can meet: on a short window the card's
	   centred offset rises towards the breadcrumb's, and without this the card
	   would pass in front of it — later in source order wins otherwise. */
	position: sticky;
	top: var(--gcp-breadcrumb-sticky-top);
	z-index: 1;
	margin: 0 0 var(--gcp-panel-gap);
	font-family: var(--gcp-font);
	font-size: var(--gcp-body-size);
	line-height: 1.3;
	letter-spacing: var(--gcp-track-tight);
	color: var(--gcp-ink);
}

.postid-6666 .wayvo-gcp-breadcrumb a {
	color: var(--gcp-ink);
	text-decoration: none;
}

.postid-6666 .wayvo-gcp-breadcrumb a:hover {
	color: #00514b;
}

/* Only the destination is underlined, as in the design — "Back to" is not. */
.postid-6666 .wayvo-gcp-breadcrumb a::after {
	content: "";
}

.postid-6666 .wayvo-gcp-breadcrumb__chevron {
	margin-right: 0.35em;
}

/* ── Panel typography ────────────────────────────────────────────────────── */

/* Weight is 500, not 700. The screenshots read heavier than the file actually
   is — worth remembering as a general caution about eyeballing weight.

   The border-bottom and padding are NOT decoration being re-stated as zero —
   they are an override. OceanWP's style.css draws a rule under every single-post
   title:

       .single .entry-title  { padding: 0 0 20px; margin: 0 0 15px;
                               border-bottom: 1px solid #f1f1f1 }   (0,2,0)

   and a product title carries `product_title entry-title` on a `.single` body,
   so it matched here and put a hairline between the title and the price. The
   original rule zeroed the margin but not the padding or the border, which is
   why the line survived.

   THE SELECTOR IS LONG BECAUSE IT HAS TO BE. OceanWP also has

       .woocommerce div.product .product_title { font-size: 24px;
                                                 margin: 0 0 10px }   (0,3,1)

   and this rule used to be `.postid-6666 .summary .product_title` at (0,3,0),
   which LOSES to it. The calibrated 40px was therefore never on the page — the
   title rendered at OceanWP's 24px with a 10px bottom margin — while every
   other property here (colour, weight, line-height, tracking, family) applied
   perfectly, because the vendor rule does not declare those. That asymmetry is
   why it went unnoticed for so long: in devtools this rule looks applied, and
   it IS applied, just not for font-size. Check the computed value per property.

   body.postid-6666 div.product div.summary .product_title is (0,4,3). Every
   qualifier is load-bearing. DO NOT SHORTEN IT. */
body.postid-6666 div.product div.summary .product_title {
	margin: 0;
	padding: 0;
	border-bottom: 0;
	font-family: var(--gcp-font);
	font-size: var(--gcp-title-size);
	font-weight: var(--gcp-title-weight);
	line-height: var(--gcp-title-lh);
	letter-spacing: var(--gcp-track-tight);
	color: var(--gcp-ink);
}

/* Same size as the title and directly beneath it, reading as a second line
   rather than as a separate price element. The export confirms this: the title
   block is 88 tall and holds two 44px lines with NO gap between them, so the
   price carries no top or bottom margin of its own — the 16px below comes from
   the gap to the description.

   SAME STORY AS THE TITLE, AND WORSE. OceanWP has

       .woocommerce div.product p.price { font-size: 36px; line-height: 30px;
                                          font-weight: 400;
                                          margin: 20px 0 0 }   (0,3,2)

   against the (0,3,1) this rule used to carry, so it lost on ALL FOUR of those
   properties and only `color` ever applied — which is precisely why the en-dash
   between the two amounts was the one character showing the right grey. The
   price was rendering at 36px/30px/400 with a 20px top margin, and the title at
   24px, so the title looked SMALLER than the price and the two sat 30px apart
   (the title's 10px bottom margin plus this 20px top margin) instead of flush.

   With both rules winning, the pair is now what the export describes: two 44px
   lines (40px x 1.1) stacked with no gap, an 88px block. The 16px below is the
   gap to the description and is the only margin either element carries. */
body.postid-6666 div.product div.summary p.price {
	margin: 0 0 var(--gcp-title-gap);
	font-family: var(--gcp-font);
	font-size: var(--gcp-title-size);
	font-weight: var(--gcp-title-weight);
	line-height: var(--gcp-title-lh);
	letter-spacing: var(--gcp-track-tight);
	color: var(--gcp-ink-soft);
}

/* THE PRICE COLOUR LIVES ON THE INNER SPANS, NOT ON THE <p>.

   A price range renders as

       <p class="price">
         <span class="woocommerce-Price-amount amount"><bdi>£10.00</bdi></span>
         &ndash;
         <span class="woocommerce-Price-amount amount"><bdi>£500.00</bdi></span>
       </p>

   and OceanWP's woocommerce.css carries a bare `.amount { color: #57bf6d }`.
   Setting `color` on `p.price` above only gets to those spans by INHERITANCE,
   and a rule that matches an element directly always beats a colour inherited
   from its parent no matter how specific the parent's selector is. So the two
   amounts rendered in OceanWP's green while the en-dash between them — the one
   character not inside a span — correctly rendered grey. That split is the tell:
   if part of a string takes your colour and part does not, a descendant is
   matching something of its own.

   `inherit` rather than a literal, so the grey stays defined in exactly one
   place: the p.price rule above.

   del/ins need the higher specificity for a different reason — OceanWP has
   `.woocommerce div.product div.summary .price del` at (0,4,2), which beat the
   (0,3,2) this rule used to carry and would repaint a sale price #ababab. Not
   reachable today (a variable product's range uses neither element) but it was
   a latent loss sitting in a rule whose whole job is this colour. */
body.postid-6666 div.product div.summary p.price .amount,
body.postid-6666 div.product div.summary p.price bdi,
body.postid-6666 div.product div.summary p.price del,
body.postid-6666 div.product div.summary p.price ins {
	color: inherit;
	font-size: inherit;
	text-decoration: none;
}

/* margin-top: 0 is the gap between the price and this text, stated from ONE
   end. OceanWP sets

       .woocommerce div.product div.woocommerce-product-details__short-description
           { margin-top: 20px }   (0,3,2)

   which outranked the (0,3,0) this rule used to carry. Adjacent siblings
   collapse to the larger of the two margins, so the 16px the price sets below
   itself was being swallowed by that 20 and --gcp-title-gap was decorative.
   Zeroing it here hands the gap back to the price, where the export defines it. */
body.postid-6666 div.product div.summary .woocommerce-product-details__short-description {
	margin-top: 0;
	font-family: var(--gcp-font);
	font-size: var(--gcp-body-size);
	line-height: var(--gcp-body-lh);
	letter-spacing: var(--gcp-track-body);
	color: var(--gcp-ink);
}

.postid-6666 .summary .woocommerce-product-details__short-description p:last-child {
	margin-bottom: 0;
}

/* ── The cart form's own rules ───────────────────────────────────────────── */

/* OceanWP draws a rule above AND below the whole add-to-cart form:

       .woocommerce div.product form.cart { padding: 20px 0;
                                            border-top: 1px solid;
                                            border-bottom: 1px solid;
                                            margin-top: 20px;
                                            border-color: #eaeaea }   (0,3,2)

   The short description sits OUTSIDE the form and the design picker is the
   first thing inside it (priority 5 on woocommerce_before_variations_form), so
   that border-top landed precisely between the description and the colour
   options — a line the design does not have.

   EVERY QUALIFIER IN THIS SELECTOR IS LOAD-BEARING. `body.postid-6666
   div.product form.cart` is (0,3,3) against OceanWP's (0,3,2). Drop `body` or
   `div` and it ties at (0,3,2), which then resolves on load order — which the
   child theme does win today, but a tie is not a decision and this page has
   already lost one silent specificity fight (see the gallery selector in the
   sticky-card section). DO NOT SHORTEN IT.

   The padding and margin are deliberately LEFT ALONE: only the line was wrong,
   and the 20px above the picker is the same rhythm as everything else here.

   Note the border-bottom is still drawn, under Add to Basket. Only the top one
   was reported, so only the top one is removed. */
body.postid-6666 div.product form.cart {
	border-top: 0;
}

/* ── Info rows ───────────────────────────────────────────────────────────── */

/* Rules above and below, matching the divider treatment in the design. The
   second row in the mock is where the Personal/Business toggle lands in phase 6.

   The export draws these as a 0.5px stroke in the muted grey, not a 1px light
   grey — noticeably finer than the screenshot reading. Each rule sits 20px above
   its line of text, and the block below carries the same 20px, which is why the
   padding is stated per-edge rather than as a single value. */
.postid-6666 .wayvo-gcp-info {
	margin-top: var(--gcp-block-gap);
	border-top: var(--gcp-line-width) solid var(--gcp-line);
	border-bottom: var(--gcp-line-width) solid var(--gcp-line);
}

.postid-6666 .wayvo-gcp-info__row {
	margin: 0;
	padding: var(--gcp-block-gap) 0;
	font-family: var(--gcp-font);
	font-size: var(--gcp-small-size);
	line-height: var(--gcp-small-lh);
	letter-spacing: var(--gcp-track-tight);
	color: var(--gcp-ink-soft);
}

.postid-6666 .wayvo-gcp-info__row a {
	color: inherit;
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

/* ── Add to basket ───────────────────────────────────────────────────────── */

.postid-6666 .summary .single_add_to_cart_button {
	display: block;
	width: 100%;
	height: var(--gcp-button-height);
	/* Sits below whatever precedes it: the Preview button once an amount has been
	   chosen, or the value control directly when the panel is still collapsed and
	   #pwgc-purchase-container is generating no box at all. */
	margin: var(--gcp-block-gap) 0 0;
	padding: 0 var(--gcp-button-pad-x);
	background: var(--gcp-ink);
	color: #ffffff;
	border: 0;
	/* Fully round rather than half the height. Same rendering at 46px, but it
	   stays a pill if the height ever changes. */
	border-radius: var(--gcp-button-radius);
	font-family: var(--gcp-font);
	font-size: var(--gcp-body-size);
	font-weight: 500;
	letter-spacing: var(--gcp-track-body);
	line-height: var(--gcp-button-height);
	text-align: center;
}

.postid-6666 .summary .single_add_to_cart_button:hover {
	background: #2a2a2c;
	color: #ffffff;
}

/* Quantity is clamped to 1 server-side by ecoswap_force_single_gift_card_quantity(),
   so the input is not shown. This is presentation only — the clamp is what
   actually enforces it.

   The Figma export DOES contain an incrementer — Frame 731 is a 360 row holding
   two 170px controls with a 20 gap, the value dropdown on the left and a
   minus / 1 / plus stepper on the right. DECIDED 2026-09-09: the stepper is not
   built, and the value control takes the full 360 on its own. This is a
   deliberate divergence from the design, not an oversight.

   Worth knowing if it is ever revisited: the bug rule 2 was written to prevent
   is now actually fixed. Pimwick mints one card per quantity as separate meta
   rows, and the theme used to read only the first, so qty 3 funded two cards
   that were never delivered. The read is now indexed per card. Building a
   working stepper would therefore be safe on the email side — it needs the clamp
   in ecoswap_force_single_gift_card_quantity() lifted, plus a re-test of the
   cart, basket and email paths. */
.postid-6666 .summary form.cart .quantity {
	display: none;
}

/* ── Design picker (phase 3) ─────────────────────────────────────────────── */

/* Overrides the 160x98 tiles in ecoswapGiftCard.css. Those rules are unscoped,
   so these need .postid-6666 plus the group to outrank them — but none of them
   are !important, so no escalation is needed. */

/* The wrapper is shared with the preview button's old styling, which drew rules
   above and below. Options needs no rule of its own: the info block below it
   already provides the divider the design shows. */
.postid-6666 .wayvo-gcp-options {
	margin: var(--gcp-block-gap) 0 0;
	padding: 0;
	border: 0;
}

/* "Options" is body size (16), not small print — it was previously set at 14 on
   the assumption that a muted grey label was the smaller of the two styles. The
   export puts it at P4 with the muted colour, and the 10px beneath it is its own
   value rather than the 20 used between blocks. */
.postid-6666 .wayvo-gcp-options .ES_emailPreview_btn_label {
	/* !important only to beat the equally-!important margin-bottom in
	   ecoswapGiftCardEmailPreview.css. */
	margin: 0 0 10px !important;
	font-family: var(--gcp-font);
	font-size: var(--gcp-body-size);
	line-height: var(--gcp-body-lh);
	letter-spacing: var(--gcp-track-body);
	font-weight: 400;
	color: var(--gcp-ink-soft);
}

.postid-6666 .ES_choiceGroup {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--gcp-swatch-gap);
	margin: 0;
	padding: 0;
	border: 0;
}

.postid-6666 .ES_choiceGroup .ES_choiceCard {
	display: block;
	width: var(--gcp-swatch-size);
	height: var(--gcp-swatch-size);
	margin: 0;
	padding: 0;
	border-radius: 50%;
	/* A hairline so the white design is still a visible circle on white. */
	border: 1px solid rgba(0, 0, 0, 0.12);
	background-image: none;
	box-shadow: none;
	cursor: pointer;
	transition: transform 0.12s ease, box-shadow 0.12s ease;
}

.postid-6666 .ES_choiceGroup .ES_choiceCard:hover {
	transform: scale(1.15);
	border-color: rgba(0, 0, 0, 0.25);
}

/* Ring drawn outside the swatch with a gap, so it reads as a selection ring
   rather than a thicker border — and works over any swatch colour. */
.postid-6666 .ES_choiceGroup input[type="radio"]:checked + .ES_choiceCard {
	border-color: transparent;
	box-shadow: 0 0 0 2px var(--gcp-surface), 0 0 0 3px var(--gcp-ink);
}

.postid-6666 .ES_choiceGroup input[type="radio"]:focus + .ES_choiceCard {
	outline: 2px solid #00514b;
	outline-offset: 3px;
}

/* The swatch's accessible name. Clipped rather than display:none, which would
   take it out of the accessibility tree along with the visual. */
.postid-6666 .wayvo-gcp-swatch__name {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
}

/* ── Value control (phase 4) ─────────────────────────────────────────────── */

/* The native denomination select and its whole variations row are hidden: the
   pill below stands in for them. The select is still in the DOM, still holds the
   value and still submits — display:none does not stop a form field posting.

   This also disposes of the collision flagged in the baseline doc. buyDinkm.css
   has `#gift-card-amount { border-radius: 10px !important }`, which phase 2 would
   have had to out-specify the way it did for the button. Hiding the element
   outright means there is nothing left to fight — no !important escalation, and
   one fewer global rule to keep track of.

   display:none rather than a visually-hidden clip, deliberately: two controls in
   the accessibility tree for one choice is worse than one, and the pill carries
   the roles. ecoswap_buyer_type.js sets display back to '' on this selector when
   returning to personal mode, which hands control back to this rule rather than
   revealing the table. */
.postid-6666 .variations_form table.variations {
	display: none;
}

/* The top margin sets the gap between the rule closing the toggle section and
   this control — the last step of the panel's rule / terms / rule / [toggle +
   corporate gifting] / rule / value rhythm, which previously had none and sat
   tight against the line above it.

   --gcp-block-gap, not a new number: it is the same 20px that every other
   rule-to-content gap in this panel uses (.wayvo-gcp-info__row's padding,
   #ecoswap-buyer-type-div's padding-top), so the spacing stays one token rather
   than becoming a per-element opinion.

   PADDING, NOT MARGIN, AND THAT IS THE POINT. This is the FIRST child of
   .woocommerce-variation-add-to-cart, which is a plain block div — nothing in
   WooCommerce, OceanWP or this theme gives it a display, a border or a padding
   of its own. A top margin on a first child of such a box collapses straight
   through it (and through .single_variation_wrap behind it) and reappears as a
   margin on an ancestor, so the gap would be measured from the wrong element and
   would move the moment anything above it changed. Padding cannot collapse, and
   this element has no background or border, so it is spatially identical and
   visually indistinguishable. */
.postid-6666 .wayvo-gcp-value {
	width: 100%;
	margin: 0 0 var(--gcp-block-gap);
	padding-top: var(--gcp-block-gap);
	font-family: var(--gcp-font);
}

/* The menu hangs off the BOTTOM of this box, so its height has to be constant.
   That is the whole reason it exists as a separate element from .wayvo-gcp-value:
   the error message is a sibling of this, not a child, because it appears and
   disappears while the control is in use and would otherwise drag the menu's
   anchor down with it every time validation failed.

   min-height is not decoration either. While the menu is open the trigger and the
   custom field are both hidden and the menu itself is out of flow, so without a
   reserved height this collapses to nothing — which would drop Add to Basket 46px
   up the page the instant the panel opened, and take the anchor with it. */
.postid-6666 .wayvo-gcp-value__control {
	position: relative;
	min-height: var(--gcp-button-height);
}

/* MUST outrank the display rules below, and does so on specificity (three
   compound selectors against two). The browser's own `[hidden] { display: none }`
   comes from the UA stylesheet and loses to ANY author rule that sets display —
   so `display: flex` on the trigger would quietly beat the hidden attribute and
   the JS would toggle a property with no visible effect. This is the rule that
   makes prop('hidden', ...) actually hide things. */
.postid-6666 .wayvo-gcp-value [hidden] {
	display: none;
}

/* Trigger and the open panel's header row are the same object visually, so they
   share every declaration except position. */
.postid-6666 .wayvo-gcp-value__trigger,
.postid-6666 .wayvo-gcp-value__head,
.postid-6666 .wayvo-gcp-value__custom {
	display: flex;
	align-items: center;
	justify-content: space-between;
	width: 100%;
	height: var(--gcp-button-height);
	margin: 0;
	padding: 0 var(--gcp-button-pad-x);
	background: var(--gcp-control-bg);
	border: 0;
	border-radius: var(--gcp-button-radius);
	font-family: inherit;
	font-size: var(--gcp-body-size);
	font-weight: 500;
	letter-spacing: var(--gcp-track-body);
	line-height: 1;
	color: var(--gcp-ink);
	text-align: left;
	cursor: pointer;
}

/* Muted until something is actually chosen — the resting state in the design. */
.postid-6666 .wayvo-gcp-value.is-empty .wayvo-gcp-value__trigger {
	color: var(--gcp-ink-soft);
}

.postid-6666 .wayvo-gcp-value__trigger:hover,
.postid-6666 .wayvo-gcp-value__head:hover {
	background: #ececec;
}

.postid-6666 .wayvo-gcp-value__trigger:focus-visible,
.postid-6666 .wayvo-gcp-value__option:focus-visible {
	outline: 2px solid #00514b;
	outline-offset: 2px;
}

/* An inline SVG path, not the usual two-borders-and-a-rotate trick.

   That trick only produces a chevron on a SQUARE box. The export's 12x6 is the
   chevron's BOUNDING box, and a 12x6 element with a left and bottom border
   rotated -45deg comes out as a tick — unequal arms meeting at the wrong angle.
   It looked exactly like a checkmark on the page. The path is unambiguous, is
   the export's dimensions literally, and takes its colour from currentColor. */
.postid-6666 .wayvo-gcp-value__chevron {
	flex: none;
	display: flex;
	align-items: center;
	justify-content: center;
	color: var(--gcp-ink-soft);
}

.postid-6666 .wayvo-gcp-value__chevron svg {
	display: block;
}

/* Points up while the panel is open. */
.postid-6666 .wayvo-gcp-value__chevron--open svg {
	transform: rotate(180deg);
}

/* ── The drop-up ─────────────────────────────────────────────────────────── */

/* Bottom-anchored, not top-anchored: the panel's last option lands exactly where
   the closed trigger was and the list grows upward from there. That is what the
   design shows, and it is why the trigger is hidden while the panel is open —
   the panel's own head row replaces it rather than sitting beneath it.

   z-index because it overlays the swatches and the terms rows above it. */
.postid-6666 .wayvo-gcp-value__menu {
	position: absolute;
	bottom: 0;
	left: 0;
	right: 0;
	z-index: 20;
	padding: 0 0 var(--gcp-block-gap);
	background: var(--gcp-control-bg);
	border-radius: var(--gcp-radius);
	/* Soft rather than dramatic — it only needs to separate one grey surface
	   from the white panel behind it. */
	box-shadow: 0 8px 24px rgba(16, 16, 17, 0.12);
}

.postid-6666 .wayvo-gcp-value__list {
	margin: 0;
	padding: 0;
	list-style: none;
}

/* Each option carries its own rule, inset from the panel edge by the same 20 as
   the horizontal padding — the design insets the dividers rather than running
   them full bleed. */
.postid-6666 .wayvo-gcp-value__option {
	display: flex;
	align-items: center;
	height: 37px;
	margin: 0 var(--gcp-button-pad-x);
	padding: 0;
	border-bottom: var(--gcp-line-width) solid #d6d6d6;
	font-size: var(--gcp-body-size);
	letter-spacing: var(--gcp-track-body);
	color: var(--gcp-ink-soft);
	cursor: pointer;
}

.postid-6666 .wayvo-gcp-value__option:last-child {
	border-bottom: 0;
}

.postid-6666 .wayvo-gcp-value__option:hover {
	color: var(--gcp-ink);
}

.postid-6666 .wayvo-gcp-value__option.is-selected {
	color: var(--gcp-ink);
}

/* ── Custom amount ───────────────────────────────────────────────────────── */

/* Pimwick's own custom-amount field is hidden, not removed: its blur handler is
   still what validates the min/max and formats the number, and this control
   writes into it and reads the result back. Removing it would mean
   reimplementing that validation here and letting the two drift apart. */
.postid-6666 #pwgc-custom-amount-form {
	display: none !important;
}

.postid-6666 .wayvo-gcp-value__prefix {
	flex: none;
	margin-right: 0.15em;
	color: var(--gcp-ink);
}

/* Unstyled on purpose: the pill around it is the control, and a second border
   inside it would read as a nested field. */
.postid-6666 .wayvo-gcp-value__input {
	flex: 1 1 auto;
	width: 100%;
	min-width: 0;
	height: 100%;
	margin: 0;
	padding: 0;
	background: none;
	border: 0;
	box-shadow: none;
	font-family: inherit;
	font-size: var(--gcp-body-size);
	font-weight: 500;
	letter-spacing: var(--gcp-track-body);
	color: var(--gcp-ink);
}

.postid-6666 .wayvo-gcp-value__input:focus {
	outline: 0;
	box-shadow: none;
}

/* Full height and a real width. It used to be a bare 12x6 sliver — the chevron
   and nothing else — which is a hard target to hit and made getting back to the
   list feel broken, especially once a validation error had drawn the eye
   elsewhere. Stretching it to the pill's full height gives roughly 28x46 to aim
   at without changing how it looks. */
.postid-6666 .wayvo-gcp-value__reopen {
	flex: none;
	display: flex;
	align-items: center;
	justify-content: flex-end;
	width: 28px;
	height: 100%;
	margin: 0;
	padding: 0;
	background: none;
	border: 0;
	cursor: pointer;
}

.postid-6666 .wayvo-gcp-value__reopen:focus-visible {
	outline: 2px solid #00514b;
	outline-offset: 2px;
}

.postid-6666 .wayvo-gcp-value.has-error .wayvo-gcp-value__custom {
	box-shadow: inset 0 0 0 1px #c0392b;
}

/* Pimwick's message, mirrored out of its hidden field so the validation stays
   in one place. Empty until there is something to say, so it takes no space. */
.postid-6666 .wayvo-gcp-value__error:empty {
	display: none;
}

.postid-6666 .wayvo-gcp-value__error {
	margin-top: 0.5rem;
	font-size: var(--gcp-small-size);
	line-height: var(--gcp-small-lh);
	letter-spacing: var(--gcp-track-tight);
	color: #c0392b;
}

/* ── Buyer type toggle (phase 6) ─────────────────────────────────────────── */

/* Personal / Business, in the second info row's slot.

   It needs no new markup and no new hook: ecoswap_add_buyer_type_toggle()
   already renders on woocommerce_before_variations_form at the default priority
   10, which lands it after the picker slot (5) and the terms row (7) — exactly
   the second row in the design. Phase 6 is therefore placement and styling, and
   the buyer-type logic itself is untouched, per rule 3 of the rebuild.

   It TAKES THE PLACE of the design's "If you would like to purchase gift cards in
   bulk, see business gifting." sentence, which is why that row was never built.
   The two say the same thing and the toggle actually does it — switching to
   Business is what reveals the bulk grid, so a link pointing elsewhere would be
   a longer route to the same screen. Where (or whether) the business gifting
   link lives on is still an open question.

   The toggle and the corporate gifting line beneath it are ONE section, so
   nothing is drawn between them — the toggle has no border and no bottom
   padding, and the row below closes the section with the only rule. The panel
   reads: rule / terms / rule / [toggle + corporate gifting] / rule / value.

   Padding is top-only for that reason. The gap down to the text is the row's own
   top padding, so there is one thing controlling it rather than two adding up. */
.postid-6666 #ecoswap-buyer-type-div {
	margin: 0;
	padding: var(--gcp-block-gap) 0 0;
	border: 0;
}

/* The corporate gifting row, which closes the toggle's section.

   .wayvo-gcp-info normally draws a rule above AND below; this one keeps only the
   bottom. A top border would put a line between the toggle and its own caption
   and split one section into two — which is exactly what it looked like before. */
.postid-6666 .wayvo-gcp-info--after-toggle {
	margin-top: 0;
	border-top: 0;
}

/* ── Disclosed fields (phase 5) ──────────────────────────────────────────── */

/* THE DISCLOSURE ITSELF IS NOT OURS AND IS NOT REBUILT HERE. Pimwick prints an
   inline `display: none` on #pwgc-purchase-container and reveals it when
   WooCommerce reports a variation, which is exactly the behaviour the design
   asks for: an empty panel until an amount is chosen (image 1), the recipient
   fields once one is (image 3). Reimplementing that would mean fighting the
   plugin for control of the same element. Phase 5 is therefore the styling of
   what gets revealed, plus its order — not a new mechanism.

   Two things make the ordering work without any JS:
   - the value control is a SIBLING of this container, not inside it, so it stays
     on screen while everything below it comes and goes;
   - #wayvo-gc-panel is display:contents, so the delivery block, the invoice
     block and the Preview button sit directly in this container's flow.

   ON THE NUMBERS: unlike the phase 4 block above, these are read off design
   image 3, which is NOT to the same scale as the image 1 export — its content
   column measures 360 but its Add to Basket comes out at 39px against the
   export's 46. Rather than trust either reading of a mock, everything here is
   expressed in the tokens the export gave us, so the two panels stay consistent
   with each other. Worth an export for image 3 before the final finesse pass. */

.postid-6666 .pwgc-field-container,
.postid-6666 #ecoswap-custom-date-div {
	margin: 0 0 var(--gcp-block-gap);
}

/* Each field's own label. Dark and full size, against the muted grey of the
   subtitles underneath — the design leans on that contrast to group them. */
.postid-6666 .pwgc-label,
.postid-6666 #ecoswap-custom-date-title,
.postid-6666 #ecoswap-delivery-time-selector-div label {
	display: block;
	margin: 0 0 8px !important;
	font-family: var(--gcp-font);
	font-size: var(--gcp-body-size);
	font-weight: 500;
	line-height: var(--gcp-body-lh);
	letter-spacing: var(--gcp-track-body);
	color: var(--gcp-ink);
}

/* Filled, not outlined. The design has no borders on any input — they read as
   soft grey wells on the white panel, the same grey as the buttons. */
.postid-6666 .pwgc-input-text,
.postid-6666 #pwgc-message,
.postid-6666 #ecoswap_custom_date {
	/* Beats Pimwick's own `.pwgc-input-text { width: 95% }`, which is printed in
	   an inline <style> inside the template. No !important on it, so specificity
	   is enough. */
	width: 100%;
	max-width: 100%;
	margin: 0;
	padding: 12px var(--gcp-button-pad-x);
	background: var(--gcp-control-bg);
	border: 0;
	border-radius: var(--gcp-radius);
	box-shadow: none;
	font-family: var(--gcp-font);
	font-size: var(--gcp-body-size);
	line-height: var(--gcp-body-lh);
	letter-spacing: var(--gcp-track-body);
	color: var(--gcp-ink);
}

.postid-6666 .pwgc-input-text:focus,
.postid-6666 #pwgc-message:focus,
.postid-6666 #ecoswap_custom_date:focus {
	outline: 2px solid #00514b;
	outline-offset: 2px;
	background: var(--gcp-control-bg);
}

.postid-6666 .pwgc-input-text::placeholder,
.postid-6666 #pwgc-message::placeholder {
	color: var(--gcp-ink-soft);
	opacity: 1;
}

/* Tall enough to invite an actual message. Pimwick's inline style sets 100px,
   which is two lines. Height rather than rows because the element is the
   plugin's; resize stays available because guessing someone's message length is
   not something to be firm about. */
.postid-6666 #pwgc-message {
	display: block;
	height: 280px;
	padding: var(--gcp-button-pad-x);
	resize: vertical;
}

/* "5000 characters remaining", "Up to a year from today", and Pimwick's
   multiple-recipients hint all come through this class. */
.postid-6666 .pwgc-subtitle {
	margin-top: 8px;
	font-family: var(--gcp-font);
	font-size: var(--gcp-small-size);
	line-height: var(--gcp-small-lh);
	letter-spacing: var(--gcp-track-tight);
	color: var(--gcp-ink-soft);
}

/* ── Segmented toggles ───────────────────────────────────────────────────── */

/* ONE treatment, THREE controls: Now/Later on the delivery date (phase 5),
   Personal/Business at the top of the panel (phase 6), and Send direct/Send to me
   in the business section (added 2026-09-11). They are the same control doing the
   same job, so they share these rules rather than each carrying a copy — a second
   copy is how two things that should match stop matching.

   The send-mode pill previously had its OWN copy in ecoswapBulkRecipients.css, in
   the old teal-and-mint palette. That copy has been deleted rather than
   re-coloured: leaving it would mean two owners for one control and the drift
   would start again. Note its track is `-inner`, not `-div` like the other two.

   A segmented pill: one grey track, the selected half filled black. Both hug
   their content rather than stretching, which is what keeps Personal/Business
   from reading as two big buttons.

   The existing rules in ecoswapGiftCard.css paint BOTH of these teal (#00514b)
   with borders, and in both cases the CHECKED state is declared `!important` on
   three properties. Specificity cannot beat !important, so the overrides below
   have to carry it too — this is the collision the baseline doc warns about, and
   the reason to check for !important before assuming a selector is wrong. */
.postid-6666 .ecoswap-delivery-time-choice-div,
.postid-6666 .ecoswap-buyer-type-choice-div,
.postid-6666 .ecoswap-delivery-choice-inner {
	display: inline-flex;
	width: auto;
	max-width: none;
	padding: 4px;
	background: var(--gcp-control-bg);
	border-radius: var(--gcp-button-radius);
}

.postid-6666 .ecoswap-delivery-time-choice-label,
.postid-6666 .ecoswap-buyer-type-choice-label,
.postid-6666 .ecoswap-delivery-choice-label {
	flex: none;
	min-width: 72px;
	padding: 8px 18px;
	background: transparent;
	border: 0;
	border-radius: var(--gcp-button-radius);
	font-family: var(--gcp-font);
	font-size: var(--gcp-body-size);
	font-weight: 500;
	line-height: 1;
	letter-spacing: var(--gcp-track-body);
	color: var(--gcp-ink-soft);
	text-align: center;
	transition: background 0.12s ease, color 0.12s ease;
}

.postid-6666 .ecoswap-delivery-time-choice-label:hover,
.postid-6666 .ecoswap-buyer-type-choice-label:hover,
.postid-6666 .ecoswap-delivery-choice-label:hover {
	background: transparent;
	color: var(--gcp-ink);
}

.postid-6666 .ecoswap-delivery-time-choice-div input[type="radio"]:checked + label,
.postid-6666 .ecoswap-buyer-type-choice-div input[type="radio"]:checked + label,
.postid-6666 .ecoswap-delivery-choice-inner input[type="radio"]:checked + label {
	background-color: var(--gcp-ink) !important;
	color: #ffffff !important;
	border: 0 !important;
}

/* The radios themselves are already display:none in ecoswapGiftCard.css, which
   is what makes the label the whole control. Restated so a focus ring can still
   be drawn on the visible half — otherwise this is keyboard-invisible. */
.postid-6666 .ecoswap-delivery-time-choice-div input[type="radio"]:focus-visible + label,
.postid-6666 .ecoswap-buyer-type-choice-div input[type="radio"]:focus-visible + label,
.postid-6666 .ecoswap-delivery-choice-inner input[type="radio"]:focus-visible + label {
	outline: 2px solid #00514b;
	outline-offset: 2px;
}

/* Native picker rather than a custom one: it is already keyboard accessible,
   already localised, and already the control people's phones expect.
   ecoswap_add_custom_date_field() used to print border-radius and margin as an
   INLINE style on this element, which nothing but !important can override; that
   attribute has been removed rather than fought, so the shared rule above is all
   this field needs. */
.postid-6666 #ecoswap-delivery-time-selector-div {
	margin-top: var(--gcp-block-gap);
}

/* ── The rule above the Preview button ───────────────────────────────────── */

.postid-6666 .wayvo-gcp-rule {
	height: 0;
	margin: var(--gcp-block-gap) 0;
	padding: 0;
	background: none;
	border: 0;
	border-top: var(--gcp-line-width) solid var(--gcp-line);
}

/* ── Preview button ──────────────────────────────────────────────────────── */

/* Now sits at the foot of the panel rather than under the swatches. Phase 5
   positions it properly once the form's order is settled. */
.postid-6666 .ES_emailPreview_btn {
	display: block;
	width: 100%;
	height: var(--gcp-button-height);
	/* Zero, because the rule above it owns that gap. Relying on this margin and
	   the rule's to collapse into one would work today and stop working the
	   moment anything above becomes a flex or grid container. */
	margin: 0;
	padding: 0 var(--gcp-button-pad-x);
	/* The export's secondary controls sit on the page ground colour even though
	   the panel behind them is white. */
	background: var(--gcp-control-bg);
	color: var(--gcp-ink);
	border: 0;
	border-radius: var(--gcp-button-radius);
	font-family: var(--gcp-font);
	font-size: var(--gcp-body-size);
	font-weight: 500;
	letter-spacing: var(--gcp-track-body);
	cursor: pointer;
}

.postid-6666 .ES_emailPreview_btn:hover {
	background: #e8e8e8;
	color: var(--gcp-ink);
}


/* ═══════════════════════════════════════════════════════════════════════════
   RESPONSIVE
   ═══════════════════════════════════════════════════════════════════════════

   THE BREAKPOINT IS DERIVED, NOT PICKED. Two columns need

       card 415 + gap 20 + panel 440 + (section padding 40 x 2) = 955

   so 960 is the first round number at which the design renders as drawn.

   ABOVE IT THERE IS NOTHING TO DO. The left column is minmax(0, 1fr), so
   between 960 and 1440 it simply shrinks from 900 to 420 while the card holds
   its 415 max and stays centred by margin-inline: auto. That is why there is no
   tablet-landscape tier here — iPad landscape (1024, 1180) IS the desktop
   layout with less whitespace either side.

   Below it, one column. Tablet PORTRAIT (768-834) and phone share that layout;
   only the type/padding tier at 600 separates them. Two layouts, not three.

   The .98 is not superstition: a viewport can land on a fractional width under
   browser zoom or on a fractional-DPI screen, and a plain 959/960 pair leaves
   that sliver matching neither query.

   WHAT THIS FIXES, beyond being narrow-friendly: the two-column grid used to
   COLLAPSE rather than stack. The panel column was a fixed 440 and the left
   column minmax(0, 1fr), so under about 460 the left column resolved to ZERO
   and the gift card image vanished outright — width 0, height 0, no error.
   "The card silently disappears" is a far worse failure than "unstyled".
   ═══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 959.98px) {

	/* Re-tokenised rather than overridden rule by rule. The tokens are declared
	   on .postid-6666, which IS the body, so restating them here reaches every
	   rule that reads one — section padding, panel padding and the gaps all move
	   together and nothing has to be hunted down individually.

	   body.postid-6666 is (0,1,1) against the original's (0,1,0), so this wins
	   on specificity rather than on source order. */
	body.postid-6666 {
		/* The panel is the page now, so its padding is page gutter, not card
		   padding: 40 either side of a 375px screen leaves 295 for content. */
		--gcp-panel-pad: 24px;
		--gcp-section-pad-x: 20px;
		/* 72 exists to pull the sticky card into its resting position at load.
		   Sticky is off below, so that reason is gone and this is just space. */
		--gcp-section-pad-top: 32px;
		--gcp-section-pad-bottom: 32px;
		/* Was the gap between the panel's blocks AND the column gap's vertical
		   counterpart. 40 is a lot of dead space on a phone. */
		--gcp-panel-gap: 24px;
	}

	/* ── One column ──────────────────────────────────────────────────────── */

	.postid-6666 div.product {
		grid-template-columns: minmax(0, 1fr);
		/* The column gap did the separating; now the row gap does. */
		row-gap: var(--gcp-panel-gap);
	}

	/* BOTH of these carry an explicit `grid-row: 1` for the desktop layout, and
	   in a single-column grid that would stack them ON TOP OF EACH OTHER in the
	   same cell rather than one after the other. Resetting to auto is not
	   tidying — leave either one out and the panel renders over the card.

	   Source order already gives the right sequence: the stage opens at priority
	   15, the breadcrumb at 16, the gallery at 20 and the stage closes at 25,
	   all before the summary. So breadcrumb, then card, then panel, with no
	   reordering needed. */
	.postid-6666 .wayvo-gcp-stage {
		grid-column: 1;
		grid-row: auto;

		/* ── THE THREE TOKENS THAT ONLY EXIST TO SERVE STICKY ──────────────
		   Each of these is load-bearing on desktop and actively harmful here,
		   and none of them fails in a way that points back at sticky.

		   min-height: --gcp-stage-height (760) gave the column something for the
		   card to travel through. With one column there is nothing to stretch
		   against and it becomes 760px of empty space under the artwork.

		   The bottom padding is --gcp-card-rest-gap, up to 320px. On desktop
		   that is the card's FLOOR — where it stops being pinned — and it costs
		   no page height because the grid stretches the stage to the panel's
		   height. Here nothing is stretching it, so the same padding is 320px of
		   blank page between the card and the form.

		   The side padding centred the card in a 900px column. There is no
		   column now. */
		min-height: 0;
		padding: 0;
	}

	/* Stays a white card on the grey ground, inset by the page gutter, rather
	   than going full-bleed. Full-bleed was written and then backed out: with no
	   mobile design to work from it is an invention, and a negative-margin
	   breakout also has to re-declare the panel's own padding, which leaves the
	   top and side padding disagreeing. Card-on-ground is the desktop treatment
	   at a different width, which is the safer reading of "make it responsive".
	   Revisit when the mobile design lands. */
	.postid-6666 div.product .summary.entry-summary {
		grid-column: 1;
		grid-row: auto;
	}

	/* ── Sticky off ──────────────────────────────────────────────────────── */

	/* A card holding the vertical centre of the viewport would sit on top of the
	   form that is now directly below it.

	   THE SELECTOR LENGTH IS LOAD-BEARING, exactly as it is on the desktop rule.
	   WooCommerce's .woocommerce div.product div.images.woocommerce-product-gallery
	   is (0,4,2) and sets position: relative; this is (0,4,3) and clears it. A
	   shorter selector here would lose on `position` alone while every other
	   property applied — the failure that cost three rounds in phase 7, and one
	   that looks in devtools like the rule is working. */
	.postid-6666 div.product div.wayvo-gcp-stage div.woocommerce-product-gallery {
		position: static;
	}

	/* Paul's call, 2026-09-11. Re-deriving --gcp-breadcrumb-sticky-top per
	   breakpoint was the alternative and it is a standing hazard: the header's
	   --wayvo-inset and --wayvo-height are declared on .wayvo-header rather than
	   :root, so they cannot be referenced from here, and the header restates
	   them at its own 768 breakpoint. Nothing would fail loudly if the two ever
	   disagreed — the breadcrumb would just tuck under the bar. Off is honest,
	   and a sticky breadcrumb costs vertical room a phone does not have. */
	.postid-6666 .wayvo-gcp-breadcrumb {
		position: static;
		margin-bottom: var(--gcp-block-gap);
	}
}


/* Phone. Type and gutters only — the layout is the one above.
   600 rather than a device width: it is where the 30px title starts costing
   more than two lines on the longest product name. */
@media (max-width: 600px) {
	body.postid-6666 {
		--gcp-title-size: 26px;
		--gcp-section-pad-x: 16px;
		--gcp-panel-pad: 20px;
		--gcp-panel-gap: 20px;
	}
}
