/* Shared components.
 *
 * This repo ships a pre-compiled Tailwind stylesheet and has no build, so
 * anything that is not already in static/dist/output.css has to be written
 * as plain CSS. Loaded from base.html, after output.css, so these rules
 * win over the utility classes they replace.
 */

/* ── The pay button ──────────────────────────────────────────────────────
 *
 * One definition for every primary action on the site. It started life on
 * the checkout, where it is THE button a customer presses to hand over
 * money, so the hero's calls to action inherit that exact shape rather
 * than approximating it - a buyer should meet the same button twice.
 *
 * The checkout's own selectors are listed here rather than restyled
 * elsewhere: #place-order-button is the single submit button on the
 * checkout, and .dg-pay-btn is what the gateway widget draws its
 * provider-rendered buttons with (GATEWAY_BUTTON_CLASS in checkout.html),
 * so Apple Pay and Google Pay match too.
 */
.btn-pay,
#place-order-button,
.dg-pay-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 10px;
    width: 100%; height: 54px; padding: 0 20px; border-radius: 5px;
    /* Stated rather than inherited: the button is dropped into pages with
       different body stacks, and it computed as Rubik on the checkout and
       Inter in the hero. "The same button" has to include its lettering. */
    font-family: Rubik, system-ui, sans-serif;
    font-size: 15px; font-weight: 700; letter-spacing: .01em; white-space: nowrap;
    background: #f5b400; color: #111827;
    box-shadow: 0 1px 2px rgba(17,24,39,.08), inset 0 -2px 0 rgba(0,0,0,.08);
    transition: background-color .12s ease, box-shadow .12s ease, transform .05s ease;
}
.btn-pay:hover:not(:disabled),
#place-order-button:hover:not(:disabled),
.dg-pay-btn:hover:not(:disabled) { background: #e8a900; color: #111827; }

.btn-pay:active:not(:disabled),
#place-order-button:active:not(:disabled),
.dg-pay-btn:active:not(:disabled) { transform: translateY(1px); box-shadow: none; }

.btn-pay:focus-visible,
#place-order-button:focus-visible,
.dg-pay-btn:focus-visible { outline: 2px solid #111827; outline-offset: 2px; }

/* Disabled keeps the brand colour - it is still THE button, just not yet -
   with a light fade and a not-allowed cursor. */
.btn-pay:disabled,
.btn-pay[aria-disabled="true"],
#place-order-button:disabled,
.dg-pay-btn:disabled { opacity: .55; box-shadow: none; cursor: not-allowed; }

.btn-pay[aria-busy="true"],
#place-order-button[aria-busy="true"] { cursor: progress; }

/* Same primary treatment for inline actions. The default pay button fills
   its container for checkout and forms; compact actions keep their natural
   width in order rows, toolbars, and side-by-side links. */
.btn-pay-compact {
    width: auto;
    min-width: 9rem;
    height: 44px;
    padding-left: 18px;
    padding-right: 18px;
    font-size: 13px;
}

#button-icon { display: inline-flex; width: 20px; height: 20px; }
#button-icon:empty { display: none; }
#button-icon svg, #button-icon img { width: 20px; height: 20px; }

/* Taller and a size up on a phone, where it is pressed with a thumb. */
@media (max-width: 639px) {
    .btn-pay,
    #place-order-button { height: 56px; font-size: 16px; }

    .btn-pay-compact {
        width: auto;
        min-width: 8.5rem;
        height: 44px;
        font-size: 13px;
    }

    /* On a phone the primary action stays reachable while the page
       scrolls: a strip at the bottom of the viewport, near-opaque so what
       scrolls underneath stays readable. One strip, two ways of holding
       it in place. */
    .pay-sticky,
    .pay-sticky-page,
    #place-order-wrap {
        bottom: 0; z-index: 30;
        padding: 12px var(--pay-sticky-bleed, 20px) 8px;
        background: rgba(255,255,255,.96);
        backdrop-filter: blur(6px);
        border-top: 1px solid #f3f4f6;
    }

    /* Inside a card: sticky, bleeding out to the card's own edges, so the
       bleed is that card's padding - 20px on the checkout. It holds for as
       long as its section is on screen, which on the checkout is the whole
       job the page has. */
    .pay-sticky,
    #place-order-wrap {
        position: sticky;
        margin-left: calc(-1 * var(--pay-sticky-bleed, 20px));
        margin-right: calc(-1 * var(--pay-sticky-bleed, 20px));
    }

    /* On the product page that is not enough: the buy box sits in a column
       that ends about 400px into a 4000px page, with the description,
       reviews and related products all below it, so a sticky strip would
       let go exactly where a buyer is still deciding. Fixed keeps it with
       them the whole way down. The strip and the button are the checkout's,
       unchanged - only what pins them differs. */
    .pay-sticky-page {
        position: fixed; left: 0; right: 0;
        /* A fixed box is placed by its MARGIN edge, so the form's own
           mb-6 held the bar 24px off the bottom and the page showed
           through the gap. */
        margin: 0;
    }


    /* Room at the end of the page so the strip never covers the last of
       it. Browsers without :has() simply do not add the gap. */
    body:has(.pay-sticky-page) { padding-bottom: 92px; }
}

/* ── Header search ───────────────────────────────────────────────────────
 *
 * The group is centred in the space beside the logo, with the WhatsApp
 * link out at the right edge. The field is what gives way when the row
 * narrows.
 */
.search-row { display: flex; align-items: center; justify-content: center; gap: 14px; }
.search-row .search-field { position: relative; flex: 0 1 420px; min-width: 0; }

/* The field itself: the magnifier sits inside it on the left, where a
   search box is read from, and the input keeps clear of both it and the
   clear button. */
.search-field { position: relative; }
.search-input {
    width: 100%; height: 40px;
    padding: 0 38px;
    font-size: 14px; line-height: 40px; color: #111827;
    /* A soft grey fill, edged only along the bottom. */
    background: #f1f2f4;
    border: 0; border-bottom: 2px solid #d1d5db; border-radius: 8px;
    transition: border-color .12s ease;
}
.search-input::placeholder { color: #9ca3af; }
/* Hover and focus only recolour the bottom edge - focus in the site's
   yellow, never green - so the field never changes size. */
.search-input:hover { border-bottom-color: #9ca3af; }
.search-input:focus,
.search-input:focus-visible {
    outline: none;
    border-bottom-color: #f5b400;
}
/* The browser's own clear affordance is replaced by the button below, so
   two of them never sit side by side. */
.search-input::-webkit-search-cancel-button { display: none; }

.search-icon {
    position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
    width: 16px; height: 16px; color: #9ca3af; pointer-events: none;
}
/* Only there when there is something to clear. */
.search-clear {
    position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
    width: 32px; height: 32px; display: flex; align-items: center; justify-content: center;
    color: #6b7280; border-radius: 5px;
}
/* One step darker than the field's fill, or the hover would not show. */
.search-clear:hover { color: #111827; background: #e5e7eb; }
.search-clear:focus-visible { outline: 2px solid #d99e00; outline-offset: 1px; }
.search-clear svg { width: 14px; height: 14px; }

/* The logo on one side and an empty box of equal flex on the other, so
   the search group sits on the page's centre line. Written here because
   lg:flex-1 is not in the pre-compiled stylesheet. */
@media (min-width: 1024px) {
    .header-side { flex: 1 1 0%; }
    /* The right-hand box carries the WhatsApp link and pushes it to the
       edge. lg:flex is not in the pre-compiled stylesheet, so the display
       is set here rather than with a utility class. */
    .header-side-right { display: flex; align-items: center; justify-content: flex-end; }
}

/* ── Description, with the related list beside it ───────────────────────
 *
 * The description runs to about 60% of the page and the rest was empty,
 * so the list sits in it. Stacked below the text on anything narrower
 * than lg, where a sidebar would only squeeze both. Written here because
 * lg:grid-cols-3 and lg:col-span-2 are not in the pre-compiled sheet.
 */
.detail-with-aside { display: grid; grid-template-columns: 1fr; gap: 2rem; }
@media (min-width: 1024px) {
    .detail-with-aside { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 3rem; }
    /* Both headings start on the same line. The description's sits 2rem
       down (its pt-8); this one is already pushed down by the panel's own
       24px padding, so the column only makes up the difference. */
    .related-aside { padding-top: calc(2rem - 1.5rem); }
}

/* A list, not a grid: one product per line, image then name, rating and
   price. The rule between rows is what separates them - no cards inside
   the card. */
.related-list { list-style: none; margin: 0; padding: 0; }
.related-list > li + li { border-top: 1px solid #f3f4f6; }
.related-item {
    display: flex; align-items: center; gap: .75rem;
    padding: .75rem 0; text-decoration: none;
}
.related-list > li:first-child .related-item { padding-top: 0; }
.related-list > li:last-child .related-item { padding-bottom: 0; }
.related-item:hover .related-title { text-decoration: underline; }
.related-thumb {
    flex: 0 0 56px; width: 56px; height: 56px;
    display: flex; align-items: center; justify-content: center;
}
.related-thumb img { max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; }
.related-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.related-title {
    font-size: 13px; font-weight: 600; line-height: 1.3; color: #111827;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.related-reviews { display: flex; align-items: center; gap: .35rem; font-size: 11px; }
.related-stars { color: #facc15; letter-spacing: .5px; }
.related-count { color: #6b7280; }
.related-price { display: flex; align-items: baseline; gap: .4rem; font-size: 13px; }
.related-price-now { font-weight: 700; color: #15803d; }
.related-price-was { font-size: 11px; color: #9ca3af; text-decoration: line-through; }

/* ── Trust badges ───────────────────────────────────────────────────────
 *
 * The markup comes from the Trust Badge model, so the shape lives here
 * and the record only names it. They close the panel beside the
 * description, under the same hairline the list rows use; with no list
 * above them there is nothing to separate, so the rule goes.
 */
.aside-badges { margin-top: 1.25rem; padding-top: 1.25rem; border-top: 1px solid #f3f4f6; }
.aside-badges:first-child { margin-top: 0; padding-top: 0; border-top: 0; }

/* Wraps rather than scrolls: the column is too narrow to hide a badge
   off the right edge and expect anyone to find it. */
.tb-row { display: flex; flex-wrap: wrap; gap: .5rem; }

/* One row per badge: mark, two lines of text, a tick. No heavy border or
   hover shadow - it is a mark of trust, not a button. */
.tb-badge {
    display: flex; align-items: center; gap: .625rem;
    flex: 1 1 auto; min-width: 0;
    padding: .5rem .75rem; border-radius: 6px;
    background: #f9fafb; text-decoration: none;
    transition: background-color .12s ease;
}
.tb-badge:hover { background: #f3f4f6; }
.tb-badge:focus-visible { outline: 2px solid #111827; outline-offset: 2px; }
.tb-mark { flex: 0 0 24px; height: 24px; display: flex; }
.tb-mark svg { width: 24px; height: 24px; }
.tb-text { display: flex; flex-direction: column; min-width: 0; }
.tb-title { font-size: 12px; font-weight: 700; color: #111827; line-height: 1.25; }
.tb-sub { font-size: 11px; color: #6b7280; line-height: 1.3; }
.tb-check { width: 16px; height: 16px; color: #16a34a; flex: 0 0 auto; margin-left: auto; }
/* The word only appears where there is room for it; the tick carries the
   meaning on its own in a narrow column. */
.tb-verify { display: none; font-size: 11px; font-weight: 600; color: #6b7280; }
@media (min-width: 480px) {
    .tb-verify { display: inline; }
}

/* Price over the quantity, so the pair reads as one block against the
   button - the checkout's row, on the product page. */
/* The price rides in the bar pinned to the bottom of a phone, above the
   quantity, so the figure travels with the button instead of being left
   at the top of a long page.
   `display: contents` keeps the wrapper out of the way everywhere else:
   the quantity control goes back to being the row's own flex item, and
   the desktop row is exactly what it was before this block existed. */
.buy-stack { display: contents; }
.buy-price { display: none; }

@media (max-width: 639px) {
    .buy-stack { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; flex: 0 0 auto; }
    .buy-price { display: flex; align-items: baseline; gap: 6px; line-height: 1.1; }
    .buy-price-now { font-size: 17px; font-weight: 700; color: #15803d; white-space: nowrap; }
    .buy-price-was { font-size: 12px; color: #9ca3af; text-decoration: line-through; white-space: nowrap; }
}


/* ── Order page ──────────────────────────────────────────────────────────
 *
 * What an order delivers - licence keys, downloads, an account handover -
 * used to sit in four panels with four different gradients. They are one
 * shape now, with the same padding and the same gap between them, so a
 * paid order and a pending one read the same way down the page. The
 * colour that distinguished them moves to the icon, where it carries
 * meaning rather than decoration.
 */
.order-deliverables { margin-top: 1rem; display: flex; flex-direction: column; gap: .75rem; }
.order-panel {
    padding: 1rem; border-radius: 8px;
    background: #f9fafb; border: 1px solid #e5e7eb;
}
/* The licence key, which is what a buyer opened this page for. A panel of
   its own, the key on a line that wraps instead of running off a phone,
   and one action beside it. Nothing coloured: the page's blues and
   purples were decoration on a page whose only real colours should be
   the status of the order. */
.licence-key {
    background: #fff; border: 1px solid #e5e7eb; border-radius: 6px;
    padding: .75rem 1rem;
}
.licence-key + .licence-key { margin-top: .5rem; }
.licence-tag {
    display: inline-block; padding: 2px 8px; border-radius: 9999px;
    font-size: 11px; font-weight: 700; letter-spacing: .02em;
}
.licence-tag-ready { background: #f1f5f9; color: #334155; }
.licence-copy {
    display: inline-flex; align-items: center; justify-content: center; gap: .375rem;
    height: 36px; padding: 0 .875rem; border-radius: 5px;
    font-size: 13px; font-weight: 600; white-space: nowrap;
    background: #111827; color: #fff;
    transition: background-color .12s ease;
}
.licence-copy:hover { background: #374151; color: #fff; }
.licence-copy:focus-visible { outline: 2px solid #111827; outline-offset: 2px; }
/* Held for two seconds after a copy, in the brand colour, so the change
   is unmistakable without a second button appearing. */
.licence-copy.is-copied { background: #f5b400; color: #111827; }

/* The deliverable panels take the page's ink; what distinguishes them is
   the icon's shape and its label, not a colour per type. */
.order-panel svg { color: #334155; }

/* ── Product page: image / purchase split ───────────────────────────────
 *
 * A 50/50 grid suits a shop selling photography. It does not suit a
 * licence box shot: the artwork is around 443px wide, so at 600px the
 * left half was a fifth empty before you count the margins baked into the
 * image file, while the right half - title, rating, description, price,
 * delivery, availability, quantity, the button and a two-column benefits
 * grid - had the same 600px and pushed the button to the bottom edge of
 * the first screen. 5:7 gives the space to the half that sells.
 */
@media (min-width: 1024px) {
    .product-split { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
}

/* The picture takes its height from the purchase column beside it.
   Left to itself the frame reserved a 4:5 box - 625px at a 500px column -
   while the title, price and button next to it came to about 420, and the
   difference showed up as a hole between the button and the row below.
   Stacked, there is no column to match, so the 4:5 box stands. */
@media (min-width: 1024px) {
    .product-split > div { min-height: 0; }
    .gallery-root, .gallery-row, .gallery-frame { height: 100%; }
    .gallery-ratio { display: none; }
}

/* The benefits row sits under both columns rather than inside the
   purchase one, so the four points read as a row across the page. The
   space above it is the gap from the button; the space below separates it
   from the description, and both scale with the viewport. */
/* A little space above the row; the negative bottom margin pulls what
   follows up under it. */
.benefits-grid { margin-top: 0.5rem; margin-bottom: -1.5rem; }

/* ── Prices ──────────────────────────────────────────────────────────────
 *
 * Every money value on the storefront is set in Rubik - the face this
 * project calls `font-serif` - rather than the Inter that body text
 * inherits. Marked once per span and set once here, so a new template
 * only has to say `price` for its figures to match the rest.
 */
.price { font-family: Rubik, system-ui, sans-serif; }

/* Icons at this size share one ink, so a row of them reads as a set
   rather than a handful of unrelated marks. Set through `color`, which
   both `fill="currentColor"` and `stroke="currentColor"` follow. */
.w-6.h-6 { color: rgb(51 65 85 / var(--tw-text-opacity, 1)); }

/* ── Feature grid icons ──────────────────────────────────────────────── */
.features-grid svg {
    color: #0a7cec;
    stroke: currentColor;
}
