/* ============================================================
   Resume4Pro — service page, real-data deviations
   ------------------------------------------------------------
   catalog-services.css is the shipped design, scoped and
   otherwise byte-faithful: tools/scope_services_css.php proves
   it reverses back to the source exactly. So every rule that
   the design does NOT contain lives here instead, in one file
   a reviewer can read end to end.

   Each block below says what live condition forces it. Nothing
   here restyles the design; it covers states the demo never had
   because the demo had one hardcoded price set, three cards per
   panel, no sticky site header and guaranteed JavaScript.

   Load order: catalog-services.css first, then this.
   ============================================================ */


/* ---- 1. Sticky site header ---------------------------------
   The design parks a card 52px below the viewport top when it
   scrolls one into view, and documents --r4p-sticky-offset as
   the hook for a host page whose header overlays that space.
   This site has one: white_navbar.blade.php renders
   <header id="sticky-menu">, which pins to the top on scroll.
   Without an offset a scrolled-to card lands underneath it.

   The stuck height is not a declared constant — it collapses
   the top bar (app.css:1893, margin-top:-57px) and keeps a
   logo-sized bottom bar — so this value is a safe floor and
   catalog-pricing-selector.js overwrites it with the header's
   measured height. Too large only adds whitespace above the
   card; too small hides the card's heading, so err high. */
:root{ --r4p-sticky-offset:84px; }

/* The ladder submits a GET form ending in #pricing-selector, so the browser's own
   fragment jump needs the same clearance the scripted scroll gets. */
.pricing-selector{ scroll-margin-top:calc(var(--r4p-sticky-offset, 0px) + 16px); }


/* ---- 2. Cards must never be permanently invisible ----------
   The design starts every .plan-card at opacity:0 and reveals
   it from an IntersectionObserver. That is fine in a demo that
   ships its own script inline; here a blocked, cached-stale or
   throwing catalog-pricing-selector.js would leave the entire
   pricing section blank — the worst possible failure on the
   page that sells things.

   So the hidden state is opt-in: it applies only once the
   section carries .is-reveal-ready, which an inline script sets
   synchronously as the section parses (no flash of visible
   cards) and removes again if no card has been revealed a few
   seconds later. Scripting off, script absent, script broken —
   all three render the cards. */
:where(.r4p-services):not(.is-reveal-ready) .plan-card{
    opacity:1;
    transform:translateY(0);
}

@media (min-width:900px){
    /* The popular card's lift is a transform too, so restoring translateY(0) above would
       otherwise drop its scale along with it. */
    :where(.r4p-services):not(.is-reveal-ready) .plan-card--popular:not(.in-view){
        transform:scale(1.045) translateY(0);
    }
}


/* ---- 3. Panels that are not three cards wide ---------------
   The design hardcodes repeat(3, 1fr), because the demo had
   exactly three cards in every panel. Live, a panel holds
   however many offerings the catalog publishes for the chosen
   career level — and the For You panel holds one featured plan
   per service type plus any bundles, which is two today with no
   bundles configured. Left in a 3-column grid those two cards
   sit left-aligned against an empty third column.

   The track width is written as the 3-up column width rather
   than a round number, so a two-card panel's cards are exactly
   as wide as a three-card panel's and the page does not appear
   to change scale between tabs. 44px is the two 22px gaps the
   design sets at this breakpoint. Four or more cards keep the
   design's behaviour and wrap onto a second row. */
@media (min-width:900px){
    :where(.r4p-services) .cards[data-card-count="1"]{
        grid-template-columns:calc((100% - 44px) / 3);
        justify-content:center;
    }

    :where(.r4p-services) .cards[data-card-count="2"]{
        grid-template-columns:repeat(2, calc((100% - 44px) / 3));
        justify-content:center;
    }
}


/* ---- 4. A ladder whose rung count is not six ---------------
   .ladder__line is inset 8.333% each side, which is half of
   100/6 — the line is drawn to stop under the centre of the
   first and last dots, for six dots. career_levels is admin-
   editable, so the count can change; the inset is handed in
   from the server as half of one rung. The fallback is the
   design's own value, so a missing variable changes nothing. */
:where(.r4p-services) .ladder__line{
    left:var(--r4p-ladder-inset, 8.333%);
    right:var(--r4p-ladder-inset, 8.333%);
}

/* The ladder is a real GET form (one <button type="submit"> per rung) rather than the
   demo's scripted tabs, because price depends on career level here and the demo's
   copy-only level switch would leave the wrong prices on screen. Some UA stylesheets
   give <form> a bottom margin. */
:where(.r4p-services) form.ladder{ margin:0; }


/* ---- 5. Career levels with nothing published yet -----------
   Five of the six live levels have no offerings. The demo had
   no such state. The rule the existing catalog already follows
   (catalog/partials/level_selector.blade.php) is: still render
   the rung, because hiding it makes the ladder look shorter
   than the service range actually is, but mark it so nobody
   clicks through expecting plans. Same decision here, in the
   ladder's own visual language. */
:where(.r4p-services) .ladder__node.is-empty .ladder__dot{
    background:var(--surface);
    border-style:dashed;
    box-shadow:none;
    opacity:.75;
}

:where(.r4p-services) .ladder__node.is-empty .ladder__select{ display:none; }

:where(.r4p-services) .ladder__tag{
    font-size:10px;
    font-weight:800;
    letter-spacing:.02em;
    text-transform:uppercase;
    color:var(--muted-strong);
    background:var(--surface);
    border:1px dashed var(--border);
    padding:1px 7px;
    border-radius:var(--radius-pill);
}

/* An is-empty rung can still be the active one — a visitor can link straight to it — so it
   keeps the active treatment and the panel below explains the gap. */
:where(.r4p-services) .ladder__node.is-active.is-empty .ladder__dot{
    background:var(--primary);
    border-style:solid;
    opacity:1;
}


/* ---- 6. An empty panel -------------------------------------
   Follows from the same thing: a chosen level with no plans for
   a service type renders a panel with no cards. Styled as a
   quiet notice rather than a card so it cannot be mistaken for
   something buyable. */
:where(.r4p-services) .panel-empty{
    margin:0;
    padding:34px 28px;
    border:1px dashed var(--border);
    border-radius:var(--radius-xl);
    background:var(--white);
    text-align:center;
    font-size:14.5px;
    font-weight:600;
    line-height:1.6;
    color:var(--muted-strong);
}


/* ---- 7. The add-to-cart status line ------------------------
   catalog.js appends .catalog-cta-note[role=status] into the
   button's parent to report what happened — the basket add
   failing is the one thing on this page a customer must not
   miss. The demo's button only ever simulated success, so the
   design has no style for it. Inside a plan card the parent is
   the card itself, so this sits directly under the CTA. */
:where(.r4p-services) .plan-card .catalog-cta-note{
    margin:0 0 12px;
    padding:9px 12px;
    border-radius:var(--radius-md);
    font-size:13px;
    font-weight:700;
    line-height:1.4;
    color:var(--primary);
    background:var(--primary-08);
}

:where(.r4p-services) .plan-card .catalog-cta-note.is-error{
    color:#9F1239;
    background:rgba(159,18,57,.08);
}

/* On the dark popular card the brand blue and the error crimson both fall below contrast,
   so both states switch to white text on a tinted panel and the error is carried by a bar
   rather than by hue alone. */
:where(.r4p-services) .plan-card--popular .catalog-cta-note{
    color:var(--white);
    background:rgba(255,255,255,.14);
}

:where(.r4p-services) .plan-card--popular .catalog-cta-note.is-error{
    color:var(--white);
    background:rgba(255,255,255,.14);
    box-shadow:inset 3px 0 0 0 #FB7185;
}


/* ---- 8. Process illustrations are uploaded images ---------
   The design draws each step as an inline <svg>, and styles the
   circular frame's contents with one rule: .process-step__illustration
   svg{width:98px;height:98px}. The CMS row carries six real uploads
   for those steps (main_demo_process_section.content), so an <img>
   has to sit in the same 98px box. object-fit:contain because the
   uploads are square PNGs with their own padding, and a crop would
   cut the illustration rather than fill the circle. */
:where(.r4p-services) .process-step__illustration img{
    width:98px;
    height:98px;
    object-fit:contain;
}


/* ---- 9. The carousel's page count is data, not two --------
   .process-track is width:200% and .process-page is width:50% in the
   design, which is a page count of exactly two. The steps come from a
   CMS row and are chunked three to a page, so a row with fewer filled
   steps yields one page and a reworded row could yield three. These
   two rules are the design's own numbers expressed as the formula they
   are: at --r4p-process-pages:2 they compute to 200% and 50%, so
   nothing changes for the shipped content.

   Only applies at and above 900px: below that the carousel unstacks
   and both track and pages are width:100% in catalog-services.css.
   Because this file loads after catalog-services.css, these width
   rules would otherwise override the mobile ones (same :where()
   specificity, later source order). */
@media (min-width:900px){
    :where(.r4p-services) .process-track{
        width:calc(var(--r4p-process-pages, 2) * 100%);
    }

    :where(.r4p-services) .process-page{
        width:calc(100% / var(--r4p-process-pages, 2));
    }
}


/* ---- 10. The FAQ answer is one or more paragraphs ----------
   The design has exactly one <p class="faq-a"> per answer and so needs
   no spacing between them. config/faqs.php stores an answer as an array
   of paragraphs, and the services set happens to be one each today —
   but the resume and linkedin sets are not, and this page's copy is
   edited in the same file. Without this a two-paragraph answer would
   run together as a single block. */
:where(.r4p-services) .faq-a + .faq-a{
    margin-top:12px;
}


/* ---- 11. Page furniture the design has no state for --------
   The admin preview banner and the currency-fallback notice are not part
   of the design — the demo has no admin and one currency. They sit above
   the first section in the page's own .container, so they need the gap
   the design's sections give each other. Not scoped with :where() like
   the rest of this file: these classes only exist on the catalog pages,
   and the wrapper is not inside .r4p-services. */
.catalog-page-notices{
    padding-top:28px;
}

.catalog-page-notices > * + *{
    margin-top:12px;
}


/* ---- 12. A long service tab label shortens on mobile --------
   The design's tabs are "For You / CV Writing / LinkedIn". Live, a service tab takes its
   label from the admin's service_types.name — and the LinkedIn row's customer-facing name
   is "LinkedIn Optimization". That sits comfortably in the roomy inline-pill tabs the
   design keeps above 640px, but at <=640px the design switches to equal-width tabs across
   the full row (catalog-services.css:189), where the long label crowds and wraps.

   The blade renders a short sibling ("LinkedIn") only for the linkedin type, and marks
   that button .tab--has-short. So the swap here is scoped to that one button: ITS full
   label is hidden and the short one shown. Every other tab has only a full label, which is
   never hidden — one where the original attempted rule (hide all .tab-label--full) made
   For You and CV Writing disappear on mobile. The label that is not on show is
   display:none, so the button's accessible name is always its visible text at every width.
   The short label defaults to display:none before the media query so desktop shows exactly
   one label per tab, as the design does. */
:where(.r4p-services) .tab-label--short{ display:none; }

@media (max-width:640px){
    :where(.r4p-services) .tab--has-short > .tab-label--full{ display:none; }
    :where(.r4p-services) .tab--has-short > .tab-label--short{ display:inline; }
}

/* The Services-page currency table; scoped to its own section. */
:where(.r4p-services).catalog-currency-prices { padding: 0 0 32px; font-family: 'Sora', sans-serif; background: var(--surface); color: var(--primary-dark); }
:where(.r4p-services.catalog-currency-prices) summary { cursor: pointer; font-weight: 700; min-height: 44px; display: flex; align-items: center; justify-content: space-between; }
:where(.r4p-services.catalog-currency-prices) summary::after { content: '+'; margin-left: 16px; }
:where(.r4p-services.catalog-currency-prices) details[open] > summary::after { content: '−'; }
:where(.r4p-services) .catalog-currency-prices__note { margin: 4px 0 12px; font-size: 14px; color: var(--muted-strong, #5B6491); }
:where(.r4p-services) .catalog-currency-prices__scroll { overflow-x: auto; }
:where(.r4p-services.catalog-currency-prices) table { width: 100%; border-collapse: collapse; font-size: 14px; }
:where(.r4p-services.catalog-currency-prices) th,
:where(.r4p-services.catalog-currency-prices) td { padding: 10px 12px; border-bottom: 1px solid #E3E7F5; text-align: left; white-space: nowrap; }
:where(.r4p-services.catalog-currency-prices) th[scope="row"] { white-space: normal; min-width: 220px; }
:where(.r4p-services.catalog-currency-prices) s { opacity: .6; margin-right: 6px; }
:where(.r4p-services.catalog-currency-prices) small { display: block; color: var(--muted-strong, #5B6491); }
:where(.r4p-services.catalog-currency-prices) td > div + div { margin-top: 8px; }
