/* tickets.fit overrides on top of the porto theme */


/* event hero
   the band uses porto's own .hero background, the same one the home page hero uses.
   Only the poster and the room for the transparent header are ours. */

.event-hero { position: relative; }

.event-hero > .container { padding-top: 100px; }

.event-hero-poster {
     width: 132px;
     height: 132px;
     border-radius: .65rem;
     background-size: contain !important;
     background-repeat: no-repeat !important;
     background-position: center !important;
     background-color: #fff;
     box-shadow: 0 .5rem 1.5rem rgba(0,0,0,.18);
     border: 3px solid #fff;
}

@media (max-width: 991px) {
     .event-hero > .container { padding-top: 84px; }

     /* the poster spans the column width on a phone, not a fixed square */
     .event-hero-poster {
          width: 100%;
          height: auto;
          aspect-ratio: 1 / 1;
     }
}


/* the ticket types card rides up over the hero band, the way the order card does in the
   mockups. Only from lg up: below that the card is d-none and a fixed bottom button
   takes over, so there is nothing to pull. */

.event-spots-card {
     position: relative;
     z-index: 2;
}

/* phone and tablet: the card floats, pinned to the bottom of the screen, so the ticket
   types and the book button stay in reach however far down the page you are. Fixed takes
   it out of flow, so the page gets padding underneath it instead. */

@media (max-width: 991.98px) {

     .event-spots-card {
          position: fixed;
          left: .5rem;
          right: .5rem;
          bottom: .5rem;
          margin-top: 0 !important;
          margin-bottom: 0 !important;
          z-index: 1031;

          /* box-shadow-2 throws downward, which a card floating at the bottom of a white
             page has nothing to cast onto, so it disappeared into the background. Throw
             it upward instead, over the content it sits on. */
          box-shadow: 0 -.5rem 2.5rem rgba(0, 0, 0, .22) !important;
     }

     /* clearance on the whole page, not just the content column, or the footer ends up
        underneath the floating card. The card measures 289px plus its 8px gap. */

     body:has(.event-spots-card) #footer { padding-bottom: 19rem; }

     /* the cart on the add-ons and checkout steps floats the same way, with the same
        card: no border, white over the bg-light it carries on a wide screen, and the
        upward shadow. Its height changes with the order, so the step's js.php measures
        it into --tf-cart-h and the step gets that much room underneath. */

     .checkout-cart {
          position: fixed;
          left: .5rem;
          right: .5rem;
          bottom: .5rem;
          z-index: 1031;
     }

     .checkout-cart > .card {
          border: 0;
          background-color: var(--bs-card-bg) !important;
          box-shadow: 0 -.5rem 2.5rem rgba(0, 0, 0, .22) !important;
     }

     #addons-display,
     #checkout-display { padding-bottom: calc(var(--tf-cart-h, 0px) + .5rem); }

     /* the fold toggle points down to fold, up to open again */

     .checkout-cart [cart-toggle] i { transition: transform .2s ease; }
     .checkout-cart [cart-toggle][aria-expanded="false"] i { transform: rotate(180deg); }
}

/* the cart starts folded (no .show) and the toggle is phone only, so from lg up the
   details always show, folded or not */

@media (min-width: 992px) {
     .checkout-cart #cart-details { display: block; height: auto !important; }
}

/* from lg up the card keeps its pull over the hero band and then tracks the page, so
   the ticket types and the book button stay in view down the whole left column.
   position: sticky replaces position: relative from the rule above; the z-index is
   unchanged, and sticky makes its own stacking context. body > .body deliberately uses
   overflow-x: clip rather than hidden, which is what keeps this working. */

@media (min-width: 992px) {

     .event-spots-card {
          margin-top: -13rem;
          position: sticky;

          /* clears the 100px sticky header */
          top: 6.25rem;
     }
}


/* spot type pricing */

#spots-available .spot-type-row { padding: .35rem 0; }
#spots-available .border-top.border-3 { border-top-width: 1px !important; opacity: .35; }

/* inventory reads as plain copy until the spots really are running out, and then only
   warms up: no red, no alarm styling. renderSpotTypePricing decides when. */

.tf-low-stock {
     color: #a05a00;
     font-weight: 600;
}


/* ─────────────────────────────────────────────────────────────────────────────
   event page body
   The sections are porto feature boxes, so there is nothing here for their layout.
   These are only the pieces porto has no equivalent for.
   ───────────────────────────────────────────────────────────────────────────── */

/* body copy: a readable measure, and paragraph rhythm for host written html */

/* a fixed measure, not a ch based one: the description carries .text-5, which porto
   resolves to 24px, and 62ch of that came out at 903px, wider than the column itself. */

.tf-prose { max-width: 42rem; }
.tf-prose p { margin-bottom: 1rem; }
.tf-prose > :last-child { margin-bottom: 0; }
.tf-prose ul, .tf-prose ol { margin-bottom: 1rem; padding-left: 1.25rem; }

/* long copy collapses to a fixed measure with the cut faded out, rather than a hard
   line clamp: -webkit-line-clamp needs display:-webkit-box, which flattens paragraphs. */

.tf-collapsed {
     position: relative;
     max-height: 12.5rem;
     overflow: hidden;
}

.tf-collapsed::after {
     content: "";
     position: absolute;
     left: 0;
     right: 0;
     bottom: 0;
     height: 4rem;
     background: linear-gradient(to bottom, rgba(255, 255, 255, 0), #fff);
     pointer-events: none;
}

/* event details: small icon and value pairs, two up once there is room for them */

.tf-detail-list {
     display: grid;
     grid-template-columns: 1fr;
     gap: .6rem 2rem;
}

@media (min-width: 576px) {
     .tf-detail-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.tf-detail {
     display: flex;
     align-items: flex-start;
     gap: .6rem;
     min-width: 0;
}

.tf-detail > i {
     color: var(--primary);
     width: 1rem;
     text-align: center;
     margin-top: .25rem;
     flex: 0 0 auto;
}

/* the organizer's avatar, with their initials underneath for when there is no image */

.tf-avatar {
     width: 3.25rem;
     height: 3.25rem;
     flex: 0 0 auto;
     border-radius: 50%;
     background-color: #fff;
     background-size: cover;
     background-position: center;
     display: flex;
     align-items: center;
     justify-content: center;
     font-weight: 700;
     color: var(--primary);
}

/* the organizer row: on a narrow screen the View organizer link was squeezing the name
   until it broke mid name ("Carlos / Diaz"), so below sm the link drops to its own line */

@media (max-width: 575.98px) {

     .tf-organizer { flex-wrap: wrap; }

     .tf-organizer > a {
          flex: 1 0 100%;
          justify-content: flex-start;
     }
}

/* the event's own url is one unbreakable token, so it set the share card's min-content
   width and pushed the card past the viewport on a phone, where body > .body clipped it */

#event-link { overflow-wrap: anywhere; }


/* dropdowns read as cards, the same treatment the offcanvas and the modal get */

.tf-dropdown-card {
     border: 0;
     border-radius: var(--bs-border-radius);
     box-shadow: var(--tf-panel-shadow);
     padding: .5rem;
}

.tf-dropdown-card .dropdown-item {
     border-radius: calc(var(--bs-border-radius) / 2);
     padding: .5rem .75rem;
}


/* app component classes the porto shell does not get
   these live in cdn/libs/css/custom.css, which is tuned for Modernize and overrides
   .btn, .card and .bg-light, so it cannot be loaded here wholesale. Ported verbatim. */

/* porto's theme-elements.css caps every offcanvas with .offcanvas { max-width: 82vw },
   which is why a fullscreen one stopped 18% short of the right edge. */

.offcanvas-fullscreen {
     --bs-offcanvas-width: calc(100% - 2rem) !important;
     --bs-offcanvas-height: calc(100% - 2rem) !important;
     --bs-offcanvas-border-radius: var(--bs-border-radius);
     border-radius: var(--bs-border-radius) !important;
     margin: 1rem;
     max-width: calc(100% - 2rem) !important;
     max-height: calc(100% - 2rem) !important;

     /* same treatment as the ticket types card: no border, porto's box-shadow-2.
        theme.css maps --bs-border-radius to var(--border-radius), so the radius above
        already tracks whatever the site's radius is set to. */
     border: 0 !important;
     box-shadow: var(--tf-panel-shadow) !important;
}

/* modals carry the site radius too */

.modal { --bs-modal-border-radius: var(--bs-border-radius); }

.modal-content { border-radius: var(--bs-border-radius); }


/* backdrops blur what is behind them, over a light dark wash.
   bootstrap ships background-color #000 with .show { opacity: .5 }, so the opacity has to
   come back up to 1 below or the wash and the blur both get faded with it. */

.offcanvas-backdrop,
.modal-backdrop,
.offcanvas-overlay-block {
     background-color: transparent !important;
     backdrop-filter: blur(10rem);
     -webkit-backdrop-filter: blur(10rem);
}

.offcanvas-backdrop.show,
.modal-backdrop.show {
     opacity: 1 !important;
}

.offcanvas-overlay-block {
     position: fixed;
     top: 0;
     left: 0;
     z-index: 9989;
     transition: all 0.3s ease-in-out !important;
}

/* the app's layering, ported from custom.css: overlay and offcanvas backdrop at 9989,
   the offcanvas itself just above at 9990, then the modal backdrop and modal on top.
   Bootstrap's own 1045/1055 sit below that overlay, which is why the panel was buried.
   Scoped to our two containers so porto's own offcanvas nav keeps its stacking. */

#offcanvas-ajax {
     z-index: 9990;

     /* bootstrap paints the offcanvas from this variable, not from background */
     --bs-offcanvas-bg: #fff;
}
#modal-ajax { z-index: 9996 !important; }

/* the modal itself carries no border, and nothing inside it does either */

#modal-ajax .modal-content {
     border: 0;
     box-shadow: var(--tf-panel-shadow);
}

#modal-ajax .card,
#modal-ajax .card-body {
     box-shadow: none !important;
}

.offcanvas-backdrop { z-index: 9989; }
.modal-backdrop { z-index: 9995 !important; }

@media (min-width: 2000px) {
     .col-xxxl-10 { flex: 0 0 auto; width: 83.333333%; }
     .col-xxxl-2  { flex: 0 0 auto; width: 16.666667%; }
}

/* h-md-100 lives inside @media (min-width: 768px) and (max-width: 991px) in custom.css.
   Ported without it, height:100% !important forced the map column to the full panel
   height on a phone and beat every attempt to give it a share of the screen. */

@media (min-width: 768px) and (max-width: 991px) {
     .h-md-100 { height: 100% !important; }
}

#offcanvas-ajax .btn-icon,
#modal-ajax .btn-icon {
     width: 2.5rem;
     height: 2.5rem;
     border-radius: 50% !important;
     padding: 0;
     margin: 0;
     align-items: center !important;
     justify-content: center !important;
     display: inline-flex;
     position: relative;
}

/* porto's theme-elements.css has .btn-icon i { margin-right: 10px }, because to porto
   .btn-icon means a button with an icon beside its label. Ours is an icon-only round
   button, so that margin pushes the glyph off centre by 5px. */

#offcanvas-ajax .btn-icon i,
#modal-ajax .btn-icon i {
     margin: 0 !important;
}

#offcanvas-ajax .btn-icon.btn-lg,
#modal-ajax .btn-icon.btn-lg {
     width: 3rem;
     height: 3rem;
     font-size: 1.5rem;
     line-height: 1.5rem;
}


/* skin-marketing-1.css sets --border-radius: 4px on :root. This sheet loads after it. */

:root {
     --border-radius: 2rem;

     /* one light tone for the whole site. Porto's .hero paints #f7f7ff, so pointing
        bootstrap's light token at it makes .bg-light, the footer and the hero band the
        same colour, and the class reusable anywhere that tone is wanted.
        Bootstrap ships #f8f9fa, porto's own --light is #FFF. */
     --bs-light: #f7f7ff;
     --bs-light-rgb: 247, 247, 255;

     /* the shadow the offcanvas and the modal both sit on. Porto's box-shadow-2. */
     --tf-panel-shadow: 0 15px 30px -15px rgba(0, 0, 0, 0.45);
}

/* theme.css:9042 redefines the class itself, not just the token:
     .bg-color-light, .bg-light { background-color: var(--light) !important; }
   with porto's --light being #FFF. That !important beats bootstrap's own .bg-light and
   any change to --bs-light, so the class has to be reclaimed here. Same selector list,
   and this sheet loads after theme.css. */

.bg-color-light,
.bg-light {
     background-color: var(--bs-light) !important;
}


/* footer sits on the bottom
   porto's .body wraps header, main and footer. On a short page the wrapper stops at the
   content and the footer floats mid-viewport, so make the wrapper fill the screen and
   let main take the slack. */

html, body { min-height: 100%; }

/* demo-marketing-1.css opens with body { overflow: hidden }, which that demo needs for
   its animated custom cursor. It makes body a scroll container, and position: sticky
   inside a scroll container that never scrolls can never stick: measured, the spots card
   sat at -238px after a 600px scroll. Horizontal overflow is held by overflow-x: clip on
   body > .body below, so nothing needs body to clip. */

body { overflow: visible; }

/* while the buyer flow panel is open the page behind it must not scroll. Bootstrap locks
   that by putting overflow: hidden on body, but theme.css gives html overflow-x: hidden,
   so body's overflow no longer passes up to the viewport and only clips body itself,
   which is as tall as the page. The lock has to go on html instead. */

html:has(#offcanvas-ajax.show) { overflow: hidden; }

body > .body {
     display: flex;
     flex-direction: column;
     min-height: 100vh;

     /* product previews are allowed to run past the container. clip, not hidden:
        hidden would make this a scroll container and break position: sticky inside it. */
     overflow-x: clip;
}

body > .body > [role="main"] { flex: 1 0 auto; }

body > .body > #footer { margin-top: auto; }

/* standard text size
   theme.css sets body to 14px, and drops it to 13px on small screens. 1rem everywhere. */

body { font-size: 1rem; }


/* buttons */

/* theme-elements.css sets .btn-modern to 0.8rem 1.5rem and demo-marketing-1.css then
   overrides it to 1.1rem 2.2rem. This sheet loads after both. */

.btn-modern { font-size: 1rem; padding: .75rem; }

/* bootstrap drives the radius through --bs-btn-border-radius, which .btn-lg points at
   --bs-border-radius-lg. Setting border-radius directly here overrides .btn's own rule,
   since this sheet loads last at equal specificity. */

.btn-lg { border-radius: var(--border-radius); }

/* flow step indicator
   spot selection -> add-ons -> checkout, in the band at the top of each step */

.flow-steps {
     display: flex;
     align-items: center;
     gap: 0;
     list-style: none;
     margin: 0;
     padding: 0;
}

.flow-step {
     display: flex;
     flex-direction: column;
     align-items: center;
     text-align: center;
     position: relative;
     padding: 0 1.35rem;
}

/* the connector sits behind the dots, drawn from each step back to the previous one */
.flow-step + .flow-step::before {
     content: "";
     position: absolute;
     top: 1.1rem;
     right: 50%;
     left: -50%;
     height: 2px;
     background-color: rgba(0,0,0,.14);
     z-index: 0;
}

.flow-step-current + .flow-step::before,
.flow-step-done + .flow-step::before {
     background-color: var(--primary);
}

.flow-step-dot {
     position: relative;
     z-index: 1;
     width: 2.2rem;
     height: 2.2rem;
     border-radius: 50%;
     display: flex;
     align-items: center;
     justify-content: center;
     font-weight: 600;
     font-size: .95rem;
     background-color: #fff;
     border: 2px solid rgba(0,0,0,.14);
     color: rgba(0,0,0,.45);
}

.flow-step-done .flow-step-dot {
     background-color: var(--primary);
     border-color: var(--primary);
     color: #fff;
}

.flow-step-current .flow-step-dot {
     border-color: var(--primary);
     color: var(--primary);
     box-shadow: 0 0 0 4px rgba(var(--primary-rgb, 111 66 193), .15);
}

.flow-step-label {
     margin-top: .45rem;
     font-size: .8125rem;
     line-height: 1.2;
     white-space: nowrap;
     color: rgba(0,0,0,.55);
}

.flow-step-current .flow-step-label {
     color: var(--primary);
     font-weight: 600;
}

@media (max-width: 575px) {
     .flow-step { padding: 0 .85rem; }
     .flow-step-label { font-size: .7rem; }
}


/* the light band the transparent header sits on
   .hero paints itself #F7F7FF and the header has no background of its own, so that tone
   is what the top of every page reads as. Reused by the marketing sections and the
   footer so the page opens and closes on the same surface. */


#footer {
     background: #F7F7FF;
     border-top-color: #F7F7FF;
}

/* theme.css points footer links at var(--default) and demo-marketing-1.css then forces
   them white for the dark footer. This sheet loads after both. */

/* .bg-light carries !important so it beats #footer's background, but theme.css also
   gives the footer a 4px border-top in var(--dark), which would leave a dark bar above
   a light footer. */

#footer.bg-light { border-top-color: var(--bs-light); }

#footer a:not(.btn):not(.no-footer-css) { color: var(--dark); }
#footer a:not(.btn):not(.no-footer-css):hover { color: var(--primary); }


/* text pages wear the same band as the event hero and need the same room under the
   transparent header, which has no background of its own to push content down. */

.page-hero > .container { padding-top: 100px; }

@media (max-width: 991px) {
     .page-hero > .container { padding-top: 84px; }
}


/* the picker on a phone
   the two columns stack, and the row is h-100, so the map stretched to the full height of
   the panel and pushed the selector and its Continue button below the viewport. Measured
   at 440x956: map 833px tall, selector starting at y=940, Continue ending at y=1757.
   Give the map a share of the screen and pin the subtotal card to the bottom. */

/* the picker fills the panel at every size
   the height chain used to rely on .h-md-100, which only exists between 768 and 991px,
   so above that the row had no definite height and the konva stage grew unbounded
   (measured 4792px). Give the wrapper its height from the flex parent instead. */

#offcanvas-ajax [spotmap-form] > .d-flex > div:first-child {
     flex: 1 1 auto;
     min-height: 0;
}

#offcanvas-ajax [spotmap-form] .row {
     min-height: 0;
}

#offcanvas-ajax .col-md-8,
#offcanvas-ajax .col-md-4 {
     min-height: 0;
}

/* the columns need a definite height, not just stretch: with height auto the selector
   column grew to its 50-row list (measured 4792px inside a 774px row) and every h-100
   inside it resolved circularly to content instead of scrolling. */

@media (min-width: 768px) {

     #offcanvas-ajax .col-md-8,
     #offcanvas-ajax .col-md-4 {
          height: 100%;
     }
}

#offcanvas-ajax [ticket-selector],
#offcanvas-ajax [ticket-selector] > .d-flex,
#offcanvas-ajax [tickets-selected],
#offcanvas-ajax [tickets-selected] > .d-flex,
#offcanvas-ajax #available-tickets,
#offcanvas-ajax #selected-tickets {
     min-height: 0;
}


@media (max-width: 767.98px) {

     /* tighter inset on a phone, where 1rem each side costs too much width */

     .offcanvas-fullscreen {
          --bs-offcanvas-width: calc(100% - 1rem) !important;
          --bs-offcanvas-height: calc(100% - 1rem) !important;
          margin: .5rem;
          max-width: calc(100% - 1rem) !important;
          max-height: calc(100% - 1rem) !important;
     }

     /* stack as real flex children so the map takes its share and the selector takes
        the rest. Without this the selector column has no bounded height, so its list
        grows past the panel and gets clipped by the rounded edge instead of scrolling. */

     #offcanvas-ajax [spotmap-form] .row {
          flex-direction: column;
          flex-wrap: nowrap;
          height: 100%;
     }

     #offcanvas-ajax .col-md-8 {
          height: 32vh;
          min-height: 0 !important;
          flex: 0 0 auto;
     }

     #offcanvas-ajax .col-md-4 {
          flex: 1 1 auto;
          min-height: 0;
     }

     /* the column is bounded now, so the total card can simply be the last flex child:
        it sits on the bottom, keeps its radius, and needs no padding underneath it. */

     #offcanvas-ajax [tickets-selected] > .d-flex > .card:first-child {
          flex: 1 1 auto;
          min-height: 0;
     }

     #offcanvas-ajax .picker-total {
          flex: 0 0 auto;
     }
}




/* the collapse toggle is a squashed rounded rectangle out of the box, with asymmetric
   padding that also held the icon off the edge. Make it a circle with the icon centred. */

@media (max-width: 991.98px) {

     #header .header-btn-collapse-nav {
          width: 2.75rem;
          height: 2.75rem;
          padding: 0;
          border-radius: 50%;
          display: inline-flex;
          align-items: center;
          justify-content: center;
          line-height: 1;
     }

     #header .header-btn-collapse-nav i {
          margin: 0;
     }
}


/* the collapsed nav reads as a card, not a flush full width band
   porto renders it edge to edge with 1px dividers between items */

@media (max-width: 991.98px) {

     #header .header-nav-main nav {
          background-color: #fff;
          border-radius: var(--bs-border-radius);
          box-shadow: var(--tf-panel-shadow);
          /* porto sets 0 30px on this element and wins without the override */
          padding: .75rem !important;
          margin: .5rem .75rem 0 .75rem !important;
          width: auto !important;
          left: 0;
          right: 0;
          overflow: hidden;
     }

     #header .header-nav-main nav > ul > li {
          border-bottom: 0;
     }

     #header .header-nav-main nav > ul > li > a {
          border-radius: calc(var(--bs-border-radius) / 2);
     }
}


/* ─────────────────────────────────────────────────────────────────────────────
   marketing page
   The home page is built on porto's demo-marketing-1 sections, so its layout,
   spacing and backgrounds all come from demo-marketing-1.css. What is left here is
   only the product previews that stand in for that demo's stock illustrations, and
   the calculator and comparison, which the demo has no equivalent of.
   Tokens as set above: --primary, --bs-light, --dark, --tf-panel-shadow.
   ───────────────────────────────────────────────────────────────────────────── */

/* type steps used inside the previews and the calculator. Porto's text-N utilities
   are em-based and inherit from whatever wraps them, which reads inconsistently at
   the small sizes these panels use. */

.tf-eyebrow {
     display: inline-block;
     font-size: .8125rem;
     font-weight: 600;
     letter-spacing: .12em;
     text-transform: uppercase;
     color: var(--primary);
     margin-bottom: 1rem;
}

.tf-h3   { font-size: 1.25rem;   line-height: 1.35; letter-spacing: -.01em; font-weight: 600; }
.tf-body { font-size: 1.0625rem; line-height: 1.7; }
.tf-meta { font-size: .875rem;   line-height: 1.5; }

/* a number that has to read as a number */
.tf-numeral { font-variant-numeric: tabular-nums; letter-spacing: -.02em; }


/* product previews
   markup, not screenshots, so they stay legible at any width and cannot go stale.
   Assembled in httpdocs/inc/ui.php and used in the hero collage and the pinned
   column of the walkthrough. */

.tf-frame {
     background-color: #fff;
     border: 1px solid rgba(0, 0, 0, .07);
     border-radius: 1rem;
     overflow: hidden;
     box-shadow: 0 24px 48px -28px rgba(33, 37, 41, .35);
}

.tf-frame-bar {
     display: flex;
     align-items: center;
     gap: .375rem;
     padding: .625rem .875rem;
     background-color: var(--bs-light);
     border-bottom: 1px solid rgba(0, 0, 0, .06);
}

.tf-frame-dot {
     width: .5rem;
     height: .5rem;
     border-radius: 50%;
     background-color: rgba(0, 0, 0, .13);
}

.tf-frame-url {
     margin-left: .5rem;
     font-size: .6875rem;
     color: rgba(0, 0, 0, .4);
     white-space: nowrap;
     overflow: hidden;
     text-overflow: ellipsis;
}

.tf-frame-body { padding: 1rem; }

.tf-ui-poster {
     aspect-ratio: 1 / 1;
     border-radius: .625rem;
     background: linear-gradient(135deg, var(--primary--200) 0%, var(--secondary) 100%);
     display: flex;
     align-items: center;
     justify-content: center;
     color: #fff;
     font-size: 1.5rem;
}

.tf-ui-row {
     display: flex;
     align-items: center;
     justify-content: space-between;
     gap: .75rem;
     padding: .5rem 0;
     font-size: .8125rem;
     border-top: 1px solid rgba(0, 0, 0, .06);
}

.tf-ui-row:first-of-type { border-top: 0; }

.tf-ui-bar {
     height: .5rem;
     border-radius: 999px;
     background-color: rgba(0, 0, 0, .07);
}

.tf-ui-btn {
     display: block;
     text-align: center;
     background-color: var(--primary);
     color: #fff;
     font-size: .8125rem;
     font-weight: 600;
     border-radius: 999px;
     padding: .5rem .75rem;
}

/* the spot map, 8 across */

.tf-ui-spots {
     display: grid;
     grid-template-columns: repeat(8, 1fr);
     gap: .25rem;
}

.tf-ui-spot {
     aspect-ratio: 1 / 1;
     border-radius: .1875rem;
     background-color: rgba(0, 0, 0, .08);
}

.tf-ui-spot-open { background-color: var(--primary-rgba-20); }
.tf-ui-spot-mine { background-color: var(--primary); }

.tf-ui-avatar {
     width: 1.5rem;
     height: 1.5rem;
     border-radius: 50%;
     background-color: var(--primary-rgba-10);
     color: var(--primary);
     font-size: .625rem;
     font-weight: 700;
     display: flex;
     align-items: center;
     justify-content: center;
     flex: 0 0 auto;
}

.tf-pill {
     display: inline-block;
     font-size: .6875rem;
     font-weight: 600;
     letter-spacing: .04em;
     text-transform: uppercase;
     border-radius: 999px;
     padding: .1875rem .625rem;
     background-color: var(--primary-rgba-10);
     color: var(--primary);
}

.tf-pill-out { background-color: rgba(0, 0, 0, .06); color: rgba(0, 0, 0, .5); }


/* payout calculator
   sits inside the demo's light rounded box. The payout is the one number on the page
   that should be unmissable. */

.tf-calc-panel {
     overflow: hidden;
     background-color: #fff;
     border: 1px solid rgba(0, 0, 0, .07);
     border-radius: var(--border-radius);
     box-shadow: 0 30px 60px -35px rgba(33, 37, 41, .4);
}

.tf-payout {
     font-size: clamp(2.75rem, 1.9rem + 2.7vw, 4rem);
     line-height: 1;
     font-weight: 700;
     letter-spacing: -.035em;
     color: var(--primary);
}

.tf-calc-line {
     display: flex;
     align-items: baseline;
     justify-content: space-between;
     gap: 1rem;
     padding: .75rem 0;
     border-top: 1px solid rgba(0, 0, 0, .07);
     font-size: 1rem;
}

.tf-calc-line:first-child { border-top: 0; }

.tf-calc-line-total {
     border-top: 2px solid var(--dark);
     margin-top: .25rem;
     padding-top: 1rem;
     font-weight: 700;
     color: var(--dark);
}

/* who pays the fees, as a two-way segmented control */

.tf-seg { display: flex; gap: .5rem; position: relative; }

.tf-seg input { position: absolute; opacity: 0; pointer-events: none; }

.tf-seg label {
     flex: 1 1 0;
     text-align: center;
     cursor: pointer;
     border: 1px solid rgba(0, 0, 0, .12);
     border-radius: 999px;
     padding: .625rem .75rem;
     font-size: .9375rem;
     font-weight: 500;
     color: var(--dark);
     transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}

.tf-seg input:checked + label {
     background-color: var(--primary);
     border-color: var(--primary);
     color: #fff;
}

.tf-seg input:focus-visible + label { outline: 2px solid var(--primary); outline-offset: 2px; }


/* comparison
   a table from md up. Below that each platform becomes its own card, because a
   four-column table on a phone is either a scroll trap or unreadable. */

.tf-compare-col-ours { background-color: var(--bs-light); }

.tf-compare table { margin: 0; }

.tf-compare th,
.tf-compare td {
     border-color: rgba(0, 0, 0, .07);
     padding: 1rem .75rem;
     vertical-align: middle;
}

.tf-compare thead th { border-bottom: 1px solid rgba(0, 0, 0, .1); }

.tf-compare .tf-compare-col-ours { border-left: 2px solid var(--primary); border-right: 2px solid var(--primary); }
.tf-compare thead .tf-compare-col-ours { border-top: 2px solid var(--primary); border-top-left-radius: .75rem; border-top-right-radius: .75rem; }
.tf-compare tbody tr:last-child .tf-compare-col-ours { border-bottom: 2px solid var(--primary); border-bottom-left-radius: .75rem; border-bottom-right-radius: .75rem; }

.tf-compare-card {
     border: 1px solid rgba(0, 0, 0, .09);
     border-radius: 1rem;
     padding: 1.25rem;
}

.tf-compare-card-ours { border: 2px solid var(--primary); background-color: var(--bs-light); }


/* faq
   porto's accordion-modern-2 at the size the rest of the page reads at */

.accordion.accordion-modern-2.tf-faq > .card + .card { margin-top: .75rem; }

.tf-faq > .card .card-header a {
     padding: 1.375rem 3.25rem 1.375rem 1.5rem;
     background-color: var(--bs-light);
     font-size: 1.0625rem;
     line-height: 1.45;
}

.tf-faq > .card .card-header a:not(.collapsed) { color: var(--primary); }

.tf-faq > .card .card-body { padding: .25rem 1.5rem 1.5rem; }


/* the pinned previews in the walkthrough
   demo-marketing-1.js cross-fades #img1/#img2/#img3 on autoAlpha and expects them
   stacked. The demo's are <img width="500">; ours are panels, so they need the width
   the attribute would have given them. */

.gsap-content-images .tf-frame { text-align: left; }


/* header anchors
   porto's data-hash plugin handles the click with an offset, but a hash typed into the
   address bar, or arrived at from another page, is a plain fragment jump the plugin
   never sees. scroll-margin-top keeps the sticky header off the section either way.
   100px matches stickyHeaderContainerHeight in the header's plugin options. */

#how-it-works,
#pricing,
#compare,
#faq { scroll-margin-top: 100px; }


/* home hero
   the home page is this section and nothing else, so it fills the viewport. svh, not
   vh, because mobile vh is measured against the browser chrome being hidden and the
   collage would sit under the fold on first paint. The vh line above it is the
   fallback for anything that does not know svh. */

.tf-hero-full {
     display: flex;
     align-items: center;
     min-height: 100vh;
     min-height: 100svh;
}

.tf-hero-full > .section-angled-content { width: 100%; }


/* home search
   one pill holding the field and the button. Below lg the button drops under the
   field, because at phone width the two side by side leave the placeholder unreadable.
   The icon is positioned against .tf-search-field, not the group, so it stays centred
   on the input once the button moves below it. */

.tf-search {
     display: flex;
     gap: .5rem;
     margin: 0 0 1.25rem;
}

.tf-search-field {
     position: relative;
     flex: 1 1 auto;
     min-width: 0;
}

.tf-search-icon {
     position: absolute;
     left: 1.25rem;
     top: 50%;
     transform: translateY(-50%);
     color: rgba(0, 0, 0, .4);
     pointer-events: none;
     z-index: 1;
}

.tf-search-input {
     height: 3.5rem;
     padding-left: 3rem;
     border-radius: 3.5rem;
     border: 1px solid rgba(0, 0, 0, .07);
     background-color: #fff;
     font-size: 1.0625rem;
     box-shadow: 0 24px 48px -28px rgba(33, 37, 41, .35);
}

.tf-search-input:focus {
     border-color: var(--primary);
     background-color: #fff;
     box-shadow: 0 0 0 .2rem var(--primary-rgba-20);
}

/* safari paints its own clear affordance inside a type=search */
.tf-search-input::-webkit-search-decoration,
.tf-search-input::-webkit-search-cancel-button { -webkit-appearance: none; }

.tf-search-btn {
     flex: 0 0 auto;
     margin: 0;
     padding-left: 1.75rem;
     padding-right: 1.75rem;
}

@media (max-width: 991px) {

     .tf-search { flex-direction: column; }

     .tf-search-btn { align-self: center; }
}
