/* Removed an @font-face for 'CameraPlainVariable' that came with the
   import: it loaded a woff2 from cdn.gpteng.co (Lovable's CDN) and the
   face was never applied to anything. Keeping it would have made every
   page load depend on a third party we do not control. The page's real
   faces are Fraunces, Inter and Caveat, loaded from Google Fonts. */

/* Home page styles, extracted from the imported Lovable markup.
   CLAUDE.md guardrail 2: no inline style attributes. The generated
   gp-hN classes below each replace one style="..." that came with the
   import - transforms, one-off backgrounds and the dot-grid gradient. */



	

	

	

	

	

	

	

	

	

	

	

	

	

	

	

	@media (prefers-reduced-motion: reduce) {
		
		
		
	}

	@media (prefers-contrast: high) {
		
		
		
	}

.gp-h1 { background-image:radial-gradient(circle, rgba(251,244,228,0.55) 1px, transparent 1.4px);background-size:26px 26px; }
.gp-h2 { transform:rotate(-3deg); }
.gp-h3 { background:#FBF4E4; }
.gp-h4 { transform:rotate(2deg); }
.gp-h5 { background:#2A1434; }
.gp-h6 { transform:rotate(-2deg); }
/* Literal, not var(--gp-amber): the home page does not load gift-flow.css,
   so that token resolves to nothing here and the rule computes to no
   background at all. */
.gp-h7 { background:#E8A33D; }
.gp-h8 { transform:rotate(3deg); }
.gp-h9 { background:#FFFFFF; }
.gp-h10 { width:80px;height:80px; }
.gp-h11 { transform:scale(1); }

/* The dancing envelope carries confetti and legs; its siblings are just an
   envelope. So inside an identical box its ENVELOPE renders 313px wide against
   their 359px - it is not smaller art, it is a fuller frame. 359/313 = 1.147,
   hence the factor.

   Measured on painted pixels: the amber body's extent in a 400px contain box,
   which is the thing the eye compares along a row.

   transform, NOT width. The first version set width:114.7% with
   max-width:none, and the max-width was the whole problem: its siblings carry
   max-width:100% and shrink with the box, so on a phone they dropped to 46px
   while the dancer stayed at 92 - literally twice the size, which is exactly
   how it looked. A transform scales whatever size the element was given at
   that breakpoint, so the ratio holds everywhere without knowing what those
   sizes are.

   It does not reflow, which is the usual reason to avoid it, but mascot-box is
   an inline-flex that centres and does not clip, so the overhang is even and
   decorative. Cropping the frame instead is not an option: tightening it to
   the envelope cuts the legs off. */
.gp-h11[src*="welcome-dancing"] {
    transform: scale(1.147);
}

.gp-h12 { transform:scale(1.25); }
/* One colour per occasion, eight of them, deliberately spaced around the wheel.
   Two tiles used to share .gp-h13 and two more shared .gp-h14, so Birthdays and
   New job were literally the same colour and so were Driving test and
   Graduation. The pairs that remain closest in hue (orange/rust, teal/deep teal)
   are separated by lightness instead, so no two circles read as a repeat. */
.gp-h13 { background:var(--amber); }        /* birthdays, warm yellow */
.gp-h14 { background:var(--teal); }         /* driving test, light blue */
.gp-h15 { background:#eda9b4; }             /* new baby, blush */
.gp-h16 { background:var(--plum); }         /* wedding, deep purple */
.gp-h17 { background:var(--orange); }       /* new job, red-orange */
.gp-h18 { background:#3f93a8; }             /* graduation, deep teal */
.gp-h23 { background:var(--orange-deep); }  /* new home, rust */
.gp-h24 { background:#8fae86; }             /* just because, sage */
.gp-h19 { background:#EAF6F9; }
.gp-h20 { background:#FCE4D6; }
.gp-h21 { background:#F1E7CE; }
.gp-h22 { background:#F7E9EC; }

/* ---- How-it-works steps on mobile ---------------------------------
   The imported markup stacks all four children (numeral, title, copy,
   mascot) in one column below md, so the big 01/02/03 and the mascot each
   took a full row with empty space beside them - about 350px per step for
   one sentence.

   Written as plain CSS rather than Tailwind utilities on purpose: the
   stylesheet that came with this page is a PREBUILT Tailwind file, so only
   classes Lovable's own markup used exist in it. col-start-3, row-span-2 and
   friends are simply absent, and adding them to the markup does nothing. */
@media (max-width: 767px) {
    .gp-step {
        display: grid;
        grid-template-columns: 2.5rem 1fr 3.25rem;
        column-gap: 14px;
        row-gap: 2px;
        align-items: start;
        padding-top: 22px;
        padding-bottom: 22px;
    }
    .gp-step > span { grid-column: 1; grid-row: 1; align-self: center; font-size: 1.6rem; }
    .gp-step > h3   { grid-column: 2; grid-row: 1; font-size: 1.15rem; }
    .gp-step > p    { grid-column: 2; grid-row: 2; font-size: 0.95rem; line-height: 1.45; }
    .gp-step > div  { grid-column: 3; grid-row: 1 / span 3; align-self: center; }
    .gp-step > div img { width: 46px; height: auto; }
}

/* ---- Mobile spacing -----------------------------------------------
   Sections carry py-20 (80px top AND bottom), which on a phone is a lot of
   empty cream between short blocks of content. Trimmed here rather than by
   swapping the Tailwind class: py-12, sm:py-20 and md:py-28 are all absent
   from the prebuilt stylesheet, so changing the class removed the padding
   entirely and nothing restored it at any width.

   Same reason the 9.6px card labels are bumped here: text-[0.7rem] does not
   exist either, so setting it silently removed their font size. */
@media (max-width: 767px) {
    main section {
        padding-top: 52px;
        padding-bottom: 52px;
    }
    /* The step list sits directly under the section heading, which already has
       its own space above it. */
    #how ol { margin-top: 28px; }
    /* Card labels: 9.6px is below a readable floor on a phone. */
    .text-\[0\.6rem\] { font-size: 0.7rem; }
}

/* ---- Hero card: the real gift-card art ----------------------------
   Replaces a "£40 JOHN LEWIS" text stand-in with the actual card image used
   everywhere else in the product (public/brands). The amount sits on the card
   as a badge rather than being baked into the art, so a different brand or
   value is a one-line change. */
.gp-giftcard {
    position: relative;
    display: inline-block;
    line-height: 0;
}
.gp-giftcard img {
    width: 150px;
    height: auto;
    border-radius: 8px;
    /* John Lewis' art is pale grey; a hairline keeps its edge readable on the
       amber panel without a heavy drop shadow. */
    box-shadow: 0 6px 14px -6px rgba(42, 20, 52, 0.45), 0 0 0 1px rgba(42, 20, 52, 0.08);
}
.gp-giftcard-amount {
    position: absolute;
    right: -6px;
    bottom: -8px;
    padding: 3px 9px;
    border-radius: 999px;
    /* Same reason as .gp-h7 above - the fallback has to be a literal. */
    background: var(--gp-plum, #3D1E4A);
    color: #FBF4E4;
    font-family: var(--gp-serif, Fraunces, Georgia, serif);
    font-size: 13px;
    font-weight: 700;
    line-height: 1.5;
    box-shadow: 0 4px 10px -4px rgba(42, 20, 52, 0.55);
}

/* Footer columns. The imported markup used md:grid-cols-[1.6fr_1fr_1fr], an
   arbitrary Tailwind value that the prebuilt stylesheet never generated, so the
   columns stacked at every width. Real rule, four columns for the Product /
   Company / Legal split. */
@media (min-width: 768px) {
    .gp-footer-grid {
        grid-template-columns: 1.6fr 1fr 1fr 1fr;
    }
}

/* ---- Hero reel: one stage, two understudies -----------------------------
   The middle slot is the only one that plays, and the only one with a play
   button. Tapping either of the others promotes it into the middle. The
   lead/side treatment belongs to the SLOT rather than the video in it, so a
   swap exchanges contents and the geometry never moves.

   Every video is preload="none" behind a poster frame: three demo videos is
   about 9MB, and a landing page that spends that before anyone asks is a
   landing page people leave. */
.gp-hero-phone {
    position: relative;
    overflow: hidden;
    border-radius: 1.75rem;
    background: var(--cream);
    box-shadow: 0 30px 60px -20px rgba(0, 0, 0, 0.7);
}
.gp-hero-phone--lead {
    padding: 0.5rem;
    box-shadow: 0 34px 70px -18px rgba(0, 0, 0, 0.75);
    outline: 1px solid rgb(251 244 228 / 0.4);
    outline-offset: -1px;
}
/* The two beside it are context, not the subject. Held back so the eye lands
   on the middle one first and reads the others as the same kind of thing
   rather than three competing claims. They now tuck UNDER the lead (negative
   margins in the markup), so the dimming also separates the overlap. */
.gp-hero-phone--side {
    padding: 0.25rem;
    opacity: 0.72;
    transition: opacity 300ms ease;
}
.gp-hero-phone--side:hover,
.gp-hero-phone--side:focus-within { opacity: 0.95; }

.gp-hero-video { display: block; }

/* No native controls on any of these, so no tap flash either: iOS paints a
   translucent grey over a tapped control, which on a video reads as the
   picture being washed out. */
.gp-hero-phone,
.gp-hero-play,
.gp-hero-swap,
.gp-hero-video { -webkit-tap-highlight-color: transparent; }
.gp-hero-video::-webkit-media-controls { display: none !important; }

/* The brand opener, over the stage while it runs. Its own element rather than
   something stitched onto the front of each clip, which is also how the real
   open page does it. */
.gp-hero-opener {
    position: absolute;
    inset: 0.5rem;               /* sits inside the lead's bezel padding */
    z-index: 2;
    width: calc(100% - 1rem);
    height: calc(100% - 1rem);
    border-radius: 1.4rem;
    /* opener-portrait.mp4 is already 9:16, so this fills the stage exactly
       and crops nothing. The landscape original (480x360) could not: covering
       it cut the envelope character in half, and containing it left bars over
       half the phone. The portrait build pads the animation into 608x1080 on
       its own cream (#FBF4E4, sampled from the source) and fades the whole
       frame to that same cream, so it resolves uniformly instead of leaving a
       band behind. See docs/demo-videos.md. */
    object-fit: cover;
    background: #FBF4E4;
    opacity: 0;
    pointer-events: none;
    /* Leaving is the message's entrance: the opener ends on its cream and
       dissolves over the already-playing message, so the message fades in
       out of it. Arriving stays quick, so a press feels immediate. */
    transition: opacity 800ms ease-out;
}
.gp-hero-opener.is-active { opacity: 1; transition-duration: 220ms; }

/* The gift, revealed when the message ends. The hero's whole claim is that a
   message and a gift arrive as one thing, and this is the only place on the
   page that shows the second half actually happening. Sized for a phone frame
   rather than borrowed from the open page's sheet, which is built for a real
   screen. */
.gp-hero-gift {
    position: absolute;
    inset: 0.5rem;
    z-index: 3;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    padding: 0.75rem;
    border-radius: 1.4rem;
    text-align: center;
    background: linear-gradient(to bottom, rgba(26, 10, 33, 0.82), rgba(26, 10, 33, 0.95));
    backdrop-filter: blur(0px);
    /* Transitioned, not animated. An animation runs the instant the element
       stops being [hidden], so the first frame is whatever the browser
       happened to paint and it reads as a snap. This starts from a declared
       resting state and is driven by a class on the next frame, so it always
       has somewhere to travel from. */
    opacity: 0;
    transform: translateY(10px) scale(0.965);
    transition: opacity 520ms ease, transform 620ms cubic-bezier(0.22, 1, 0.36, 1),
                backdrop-filter 520ms ease;
}
.gp-hero-gift.is-in {
    opacity: 1;
    transform: none;
    backdrop-filter: blur(4px);
}
/* The contents follow the panel rather than arriving with it, which is what
   stops a card this small feeling like it was stamped on. */
.gp-hero-gift-eyebrow,
.gp-hero-gift-art,
.gp-hero-gift-amount {
    opacity: 0;
    transform: translateY(8px);
    transition: opacity 420ms ease, transform 520ms cubic-bezier(0.22, 1, 0.36, 1);
}
.gp-hero-gift.is-in .gp-hero-gift-eyebrow,
.gp-hero-gift.is-in .gp-hero-gift-art,
.gp-hero-gift.is-in .gp-hero-gift-amount {
    opacity: 1;
    transform: none;
}
.gp-hero-gift.is-in .gp-hero-gift-eyebrow { transition-delay: 120ms; }
.gp-hero-gift.is-in .gp-hero-gift-art     { transition-delay: 190ms; }
.gp-hero-gift.is-in .gp-hero-gift-amount  { transition-delay: 290ms; }
/* Sized up from 0.5rem / 1rem. The card is the payoff of the whole sequence
   and it was being announced in type smaller than a caption, inside a phone
   frame that is already small on a laptop. The card art is unchanged, so the
   panel's proportions still hold. */
.gp-hero-gift-eyebrow {
    margin: 0;
    font-size: 0.65rem;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--amber);
}
/* No src until the reveal fills it in, so the element must not occupy space
   or paint a broken-image glyph before then. src="" used to be in the markup,
   which is a PRESENT but empty source: browsers treat that as a failed image,
   and historically re-requested the page itself. */
.gp-hero-gift-art:not([src]) { display: none; }
.gp-hero-gift-art {
    width: 82%;
    max-width: 150px;
    height: auto;
    border-radius: 0.5rem;
    box-shadow: 0 10px 24px -10px rgba(0, 0, 0, 0.8);
}
.gp-hero-gift-amount {
    margin: 0;
    font-family: var(--gp-serif, "Fraunces", Georgia, serif);
    font-size: 1.35rem;
    line-height: 1.15;
    font-weight: 600;
    color: var(--cream);
}
/* "Voucher" sits on its own line under the amount and brand. Appended by
   hero-reel.js, so it is display:block here rather than a <br> in markup that
   does not exist until a slide reveals. */
.gp-hero-gift-voucher {
    display: block;
}
/* The side phones never play, so they never reveal. */
.gp-hero-phone--side .gp-hero-gift { display: none; }
/* The name plate would sit under the panel; the gift says whose it is. */
.gp-hero-phone.is-gifted .gp-hero-name { display: none; }

/* The message's fade-in is the opener dissolving over it (.gp-hero-opener
   above). It used to be an animation on the video keyed to is-playing, which
   is set when play is PRESSED, so it ran under the opener during its first
   half second and was long over by the time the message actually appeared. */

/* The exchange, so a swap reads as deliberate rather than as a flicker. */
.gp-hero-phone.is-swapping .gp-hero-video { animation: gp-hero-swap-in 320ms ease; }
@keyframes gp-hero-swap-in {
    from { opacity: 0.25; transform: scale(0.97); }
    to   { opacity: 1;    transform: scale(1); }
}

/* Play: the lead only. A real <button>, not a handler on the video, so it is
   reachable by keyboard and announced. */
.gp-hero-play,
.gp-hero-swap {
    position: absolute;
    inset: 0;
    width: 100%;
    cursor: pointer;
    border: 0;
    background: transparent;
    color: #fff;
}
.gp-hero-play { display: grid; place-items: center; }
.gp-hero-play::before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, rgba(26, 10, 33, 0.55), rgba(26, 10, 33, 0.05) 55%);
}
.gp-hero-play svg {
    position: relative;
    box-sizing: content-box;
    width: 30px;
    height: 30px;
    padding: 1.15rem;
    padding-left: 1.3rem; /* optically centres a triangle in a circle */
    border-radius: 999px;
    background: var(--orange);
    box-shadow: 0 12px 28px -8px rgba(0, 0, 0, 0.65);
    transition: transform 240ms ease;
}
.gp-hero-play:hover svg { transform: scale(1.06); }

/* View: the sides only. No play triangle on them, because pressing one does
   not start anything, it changes what is on stage. Saying "play" there would
   promise a thing that does not happen. */
.gp-hero-swap {
    display: flex;
    align-items: flex-end;
    justify-content: center;
    padding-bottom: 0.45rem;
}
.gp-hero-swap-hint {
    font-size: 0.5rem;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: rgb(251 244 228 / 0.95);
    background: rgb(26 10 33 / 0.55);
    padding: 0.22rem 0.5rem;
    border-radius: 999px;
    opacity: 0;
    transition: opacity 240ms ease;
}
.gp-hero-phone--side:hover .gp-hero-swap-hint,
.gp-hero-swap:focus-visible .gp-hero-swap-hint { opacity: 1; }

.gp-hero-play:focus-visible,
.gp-hero-swap:focus-visible { outline: 3px solid var(--amber); outline-offset: 3px; }

/* Each slot shows only the control that belongs to its position. */
.gp-hero-phone--lead .gp-hero-swap { display: none; }
.gp-hero-phone--side .gp-hero-play { display: none; }
.gp-hero-phone.is-playing .gp-hero-play { display: none; }

.gp-hero-name {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0.55rem;
    z-index: 1;
    font-size: 0.9rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    text-align: center;
    color: rgb(251 244 228 / 0.92);
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.6);
    pointer-events: none;
}
/* Up from 0.5rem, which computed to 8px: roughly 3mm on a real handset, and
   these are the names of the people whose videos they are, not decoration.
   The centre card was 11.2px and is now 14.4px; the side cards track it. */
.gp-hero-phone--side .gp-hero-name { font-size: 0.75rem; bottom: 1.5rem; }
.gp-hero-phone.is-playing .gp-hero-name { display: none; }

/* An 8deg turn adds roughly height x sin(8deg) to a phone's bounding box, so
   three that fit on paper did not fit on a screen. Scale the row rather than
   shrink the phones, and the proportions the design depends on survive. */
/* The phones are sized to fill a 390px screen, so narrower ones scale the row
   down rather than shrinking the phones further: the proportions the design
   depends on survive and only the whole thing gets smaller. A 7deg turn also
   adds roughly height x sin(7deg) to each side phone's bounding box, which is
   why three that add up on paper still need the headroom measured. */
@media (max-width: 384px) {
    [data-gp-hero-reel] { transform: scale(0.93); transform-origin: bottom center; }
}
@media (max-width: 345px) {
    [data-gp-hero-reel] { transform: scale(0.82); }
}

/* The hero reveal is deliberately NOT gated on prefers-reduced-motion.
   It used to be: the confetti sat out and the card snapped into place. That is
   the decision here, and it is a decision rather than an oversight, so nobody
   needs to wonder whether the block below went missing. Leaving the confetti
   unconditional while the card still snapped would have been the incoherent
   half of it, so both play for everyone.

   The rest of the site still honours the setting: the open page's curtain and
   the photosensitivity care around the deck are untouched by this. */
