/* ==========================================================================
   TORQA — one of the SIX FORK layouts in Constants.HOME_PAGE_DESIGNS: a
   different KIND of page from the seven token-set designs, sold in the
   merchant's APPEARANCE picker as "Cinematic".

   Design notes: design-reference/torqa/torqa-design-system.md.

   Served by the ONE shell, templates/bookingHomeDesigns.html, on the public
   /home/{accountName} route whenever the merchant has picked it:
   BookingPageServices.applyHomePageLayout resolves the design and the shell
   emits THIS sheet, bookingHomeTorqaView.js and #pt-torqa-root from the
   BookingHomeLayout.TORQA row (markup in bookingHomeTorqaTemplate.html). It has
   no controller and no reserved URL of its own — /home/{slug}?design=torqa is
   the preview.

   ⚠️ THIS SHEET LOADS AFTER bookinghome-designs.css, and that is load-bearing.
   The template reuses EVERY base hook (#ptsLang, #ptsReviewList, .pts-cat-more,
   #ptsHoursList, the dialogs, …), so the base sheet's element-level `.pts-*`
   rules reach all of them — which is what § 20 BASE OVERRIDES at the foot of
   this file exists to neutralise. Renaming a hook in the template means
   deleting its override there.

   ==========================================================================
   § 0 · THE FIVE HOUSE RULES THIS FILE OBEYS (each has cost a bug before)
   ==========================================================================
   1. 🚫 FLEXBOX ONLY — no CSS Grid anywhere. The public booking page has to
      serve Opera, and it cannot degrade to one unstyled column. Every
      `repeat(N,minmax(0,1fr))` is `flex:0 1 var(--basis)` + `min-width:0`.
      ⚠️ `flex:0 1`, NEVER `1 1`, in a wrapping row: a growable cell stretches
      the last short line across the whole row, and neither the 4-up catalogue
      nor the 4-up team divides evenly at every count.
   2. 🚫 NO color-mix(). It is Baseline 2023, roughly two years newer than
      anything else this page needs, and inside a custom property it fails at
      SUBSTITUTION time — the declaration becomes invalid at computed-value
      time and the property resets to its INITIAL value, i.e. `background`
      becomes `transparent`. Every derived shade here is a precomputed literal
      or an rgba() over a stated channel triplet.
   3. 🔑 A MEDIA QUERY ADDS NO SPECIFICITY. Every responsive override below
      matches the anchoring of the rule it overrides, or the base rule silently
      wins and the page keeps its desktop measure on a phone.
   4. 🔑 EVERY RULE THAT STYLES A LINK'S OR BUTTON'S TEXT CARRIES `.ptt-shell`.
      The base's own `.pt-home-shell a{color:inherit;font-size:inherit}` and
      `.pt-home-shell button{font:inherit}` are (0,1,1); a bare `.ptt-btn` is
      (0,1,0) and LOSES to them — which on a sibling fork produced an invisible
      button label, a CTA in the wrong face and the wrong weight, all at once.
      `newbookingpage.css` additionally leaks a bare
      `a{font-weight:400;font-size:16px;color:#1e1e1e}` and a universal
      `*{font-family:'Poppins'}` into this page.
      ⚠️ Corollary: a change here can only be verified against the shell's FULL
      sheet list (fontello → sweetalert → newbookingpage → newbookingpagetheme
      → bookinghome-designs → this file). This file alone reports the DESIGNED
      values, not the rendered ones.
   5. 🔑 THE BAND RHYTHM IS SHELL-PREFIXED PADDING, never a bare class and never
      margin. `.pt-home-shell section{padding:0}` is (0,1,1) and out-ranks a
      bare `.ptt-band` (0,1,0), so a bare padding rule measures ZERO between
      every band and the whole page renders as one unbroken column — with
      nothing failing. `.ptt-shell .ptt-band` is (0,2,0) and wins. It has to be
      padding rather than the base's own margin answer, because a band here
      carries a BACKGROUND and the ground must extend through the rhythm.

   § 0a · THE ACCENT IS THE MERCHANT'S BOOKING PAGE THEME, STATED AS TWO
          SOLVED VALUES
   ----------------------------------------------------------------------
   🔑 THIS LAYOUT STATES NO ACCENT OF ITS OWN. The accent is whichever of the
   ten swatches the merchant picked in online booking → BOOKING PAGE THEME,
   which the base resolves and writes as `data-pts-accent` on the shell — so
   this page wears the same colour as the booking flow it hands off to, exactly
   like Rounded/Dentara. It used to hard-code its reference's teal (#128078)
   AND overwrite the base's `--pts-accent*` with it, so a themed account got a
   teal page; that direction is now REVERSED — see § 1a.

   What survives the change is the SHAPE of the problem, which is this
   layout's own: its grounds ALTERNATE (an ink hero and closing band bracketing
   a white middle), so one accent value cannot serve both. Measured on the old
   teal, which is why the split exists:
     #128078 on #ffffff  =  4.79:1 ✓  (white label ON the fill; the light-band
                                       FILL, e.g. every primary button)
     #0f7b73 on #ffffff  =  5.12:1 ✓  (teal as TYPE on a light ground)
     #128078 on #0e1013  =  3.98:1 ✗  (…which is why the fill is not reused as
                                       type on the dark bands)
     #2fb3a6 on #0e1013  =  7.37:1 ✓  (teal as TYPE on ink)
     #2fb3a6 on #ffffff  =  2.59:1 ✗  (…and why the lifted value is dark-ground
                                       ONLY)
   So: --ptt-accent is a FILL, --ptt-accent-ink is type on light, and
   --ptt-accent-lift is type on ink. ⚠️ Never use --ptt-accent as a foreground
   and never use --ptt-accent-lift on a light band.
   ⚠️ And a label ON the fill is --pts-accent-on, NEVER a literal #fff: white on
   the yellow swatch is 1.8:1, which is the whole reason that token exists.

   Other measured pairs used below:
     #15171a on #ffffff  = 17.9:1 ✓  headings
     #5b6167 on #ffffff  =  6.27:1 ✓  body
     #6b7279 on #ffffff  =  4.87:1 ✓  muted (the floor — nothing lighter carries
                                       text)
     #a8b0b6 on #0e1013  =  8.67:1 ✓  body on ink
     #1a7f4b on #ffffff  =  5.02:1 ✓  open
     #c0392b on #ffffff  =  5.44:1 ✓  closed
   ========================================================================== */


/* ==========================================================================
   1 · TOKENS
   ⚠️ Declared on the SHELL, never on :root. A custom property set on the
   element itself always beats one inherited from an ancestor, so a
   `:root{--ptt-…}` override inside a media query is INERT against a shell that
   declares its own value — the desktop scale then survives all the way down to
   320px and the page simply never responds. Every responsive override below is
   therefore anchored as `.ptt-shell` too.
   ========================================================================== */
.ptt-shell{
	--ptt-display:'Archivo',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
	--ptt-body-font:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;

	/* § 1a · THE ACCENT READS THE BASE'S THEME TOKENS — see § 0a.
	   🔑 THE ARROW POINTS THIS WAY, and it used to point the other. Six of the
	   seven are the base's own per-theme values, which `[data-pts-accent=…]`
	   sets on this very element; the seventh is solved below, per theme.
	   ⚠️ SO THE BASE-PALETTE BLOCK BELOW MUST NOT MAP ANY `--pts-accent*` BACK
	   ONTO THESE — `--ptt-accent-ink:var(--pts-accent-ink)` beside
	   `--pts-accent-ink:var(--ptt-accent-ink)` is a dependency CYCLE, which
	   makes BOTH invalid at computed-value time, i.e. every accented surface
	   on the page falls back to its initial value (a transparent background,
	   not a fallback colour). That block now carries surfaces and semantics
	   only. */
	--ptt-accent:var(--pts-accent);
	--ptt-accent-ink:var(--pts-accent-ink);
	--ptt-accent-deep:var(--pts-accent-hover);
	--ptt-accent-soft:var(--pts-accent-soft);
	--ptt-accent-edge:var(--pts-edge-30);
	--ptt-accent-rgb:var(--pts-accent-rgb);
	/* The lifted value has NO base equivalent: the base solves its accent for a
	   LIGHT page, and its one dark-ground value (`--pts-accent-ink` under
	   `[data-pts-theme="dark"]`) is scoped to the dark APPEARANCE, which this
	   light page never carries. Default-theme fallback here, overridden per
	   theme in § 1b. */
	--ptt-accent-lift:#9a9a9a;

	--ptt-ink:#0e1013;
	--ptt-ink-2:#17191d;
	--ptt-ink-3:#24272c;
	--ptt-on-ink:#ffffff;
	--ptt-on-ink-body:#a8b0b6;
	--ptt-on-ink-line:rgba(255,255,255,.14);

	--ptt-title:#15171a;
	--ptt-copy:#5b6167;
	--ptt-muted:#6b7279;
	--ptt-line:#e4e7ea;
	--ptt-surface:#ffffff;
	--ptt-surface-2:#f6f7f9;

	--ptt-success:#1a7f4b;
	--ptt-success-bg:#e4f4eb;
	--ptt-danger:#c0392b;
	--ptt-danger-bg:#fbeceb;

	--ptt-r:10px;
	--ptt-r-lg:16px;
	--ptt-r-sm:8px;
	--ptt-r-pill:999px;

	--ptt-page:1200px;
	--ptt-gutter:24px;
	--ptt-band-pad:84px;
	--ptt-gap:20px;

	/* 4-up rows. The basis is solved from the gap so the row divides exactly;
	   every responsive block re-points BOTH the basis and its gap count. */
	--ptt-card-basis:calc((100% - (var(--ptt-gap) * 3)) / 4);
	--ptt-rev-sum:320px;
	--ptt-map-basis:calc(58% - 10px);
	--ptt-loc-basis:calc(42% - 10px);
	/* ⚠️ Solved against a 12px gap — see the `column-gap` note on the hours row's
	   own rule, which is what makes the real gap 12px. */
	--ptt-hours-basis:calc((100% - 36px) / 4);

	--ptt-h1:64px;
	--ptt-h2:32px;
	--ptt-h3:17px;
	--ptt-fs-body:15px;
	--ptt-fs-meta:13px;
	--ptt-fs-cap:12px;
	--ptt-fs-micro:11px;

	--ptt-move:.22s cubic-bezier(.4,0,.2,1);

	/* ==================================================================
	   🔑 THE BASE'S OWN PALETTE — SURFACES AND SEMANTICS ONLY.

	   This block is not tidiness — it is the only thing that makes the base
	   COMPONENTS this template reuses sit on this page's GROUNDS. The base
	   paints the map art, the map pin, the "View in Map" pill, the star bars,
	   the review chips, the status badges, the credit badge and the shared
	   dialogs from `--pts-*`, so without the surface/type/semantic mappings
	   below a base component renders on the base's own greys inside this
	   layout's bands.
	   🔑 Mapping tokens rather than restating the rules is also what makes a
	   base component ADDED LATER wear this layout's grounds for free.

	   🔑 THE ACCENT IS NO LONGER PART OF IT. This block used to map every
	   `--pts-accent*` onto this layout's teal, which was precisely what
	   overrode the merchant's BOOKING PAGE THEME across the whole page — the
	   defect that made the base's own accent tokens the RIGHT source rather
	   than the wrong one. Since the accent flows base → fork (§ 1a), the base's
	   per-theme values are already correct on this element and there is nothing
	   to re-point. ⚠️ The historic note that "--pts-accent-ink maps to this
	   layout's INK, never the fill" survives in the opposite direction, and
	   still matters: --ptt-accent-ink reads --pts-accent-ink, which is the
	   base's token for accent TEXT, and neither may be pointed at the fill.
	   ================================================================== */
	/* ⚠️ NOTHING ACCENT-DERIVED IS RE-POINTED HERE ANY MORE — not
	   `--pts-accent`, `-on`, `-rgb`, `-soft`, `-ink`, `-hover`, `-fill`,
	   `-avatar-tint`, nor the five `--pts-edge-*`. They were this layout's
	   teal, which is exactly what overrode the merchant's swatch; the arrow now
	   runs the other way (§ 1a) and every one of them is the base's own
	   per-theme value, already correct on this element. Re-adding a single one
	   either re-pins the page to one colour or, if it reads a `--ptt-accent*`,
	   makes both ends of the pair invalid — see the cycle warning in § 1a. */
	--pts-header-tint:var(--ptt-surface-2);
	/* Surfaces + type, so a base component sits on this page's grounds. */
	--pts-bg:var(--ptt-surface-2);
	--pts-surface:var(--ptt-surface);
	--pts-surface-2:var(--ptt-surface-2);
	--pts-text:var(--ptt-title);
	--pts-muted:var(--ptt-muted);
	--pts-line:var(--ptt-line);
	--pts-radius:var(--ptt-r);
	/* Semantic pairs — the status pills and the reviews' own marks. */
	--pts-success:var(--ptt-success);
	--pts-danger:var(--ptt-danger);
	--pts-badge-open-bg:var(--ptt-success-bg);
	--pts-badge-open-today-bg:var(--ptt-success-bg);
	--pts-badge-closed-bg:var(--ptt-danger-bg);
	--pts-badge-closed-today-bg:var(--ptt-danger-bg);
}

/* ==========================================================================
   1b · THE LIFTED ACCENT, ONE SOLVED VALUE PER THEME
   The only accent token this layout states for itself, because it is the one
   the base has no answer for: the accent as TYPE on this page's ink bands
   (the hero eyebrow + fact glyphs on --ptt-ink).

   🔑 SOLVED AGAINST #17191d (--ptt-ink-2), the ground the footer wore before it
   became white — for light type on a dark ground a LIGHTER ground is the harder
   case, so every value below clears the hero's darker --ptt-ink with ~0.5 to
   spare. Re-derive against --ptt-ink only if that margin is ever wanted back;
   the footer is light now and uses --ptt-accent-ink, never this token.

   The rule is the base's own: the smallest 5% step of the theme's accent mixed
   toward WHITE that clears 5.5:1, i.e. AA plus a margin for the 12px uppercase
   eyebrow that is the smallest type wearing it. Three themes are already light
   enough and take their accent UNCHANGED, which is the point of deriving
   rather than picking — a flat mix would wash those out for nothing.
   ⚠️ Re-derive with the same rule if a swatch in the base's
   `[data-pts-accent=…]` blocks ever moves; these are its values, not free ones.
        theme      mix   on #17191d / #0e1013
        default    55%     6.25 / 6.77
        teal       25%     5.66 / 6.13
        violet     40%     5.81 / 6.29
        pink       10%     5.56 / 6.02
        green       0%     6.11 / 6.61
        orange      0%     5.69 / 6.16
        blue       15%     5.61 / 6.07
        darkblue   55%     5.62 / 6.08
        yellow      0%     9.78 / 10.58
        red        45%     5.89 / 6.38
   ========================================================================== */
.pt-home-shell[data-pts-accent="default"].ptt-shell{--ptt-accent-lift:#9a9a9a}
.pt-home-shell[data-pts-accent="teal"].ptt-shell{--ptt-accent-lift:#40a0a0}
.pt-home-shell[data-pts-accent="violet"].ptt-shell{--ptt-accent-lift:#b080d9}
.pt-home-shell[data-pts-accent="pink"].ptt-shell{--ptt-accent-lift:#e3688d}
.pt-home-shell[data-pts-accent="green"].ptt-shell{--ptt-accent-lift:#41ac5e}
.pt-home-shell[data-pts-accent="orange"].ptt-shell{--ptt-accent-lift:#e76f51}
.pt-home-shell[data-pts-accent="blue"].ptt-shell{--ptt-accent-lift:#419ac9}
.pt-home-shell[data-pts-accent="darkblue"].ptt-shell{--ptt-accent-lift:#8e8eb7}
.pt-home-shell[data-pts-accent="yellow"].ptt-shell{--ptt-accent-lift:#eeba0b}
.pt-home-shell[data-pts-accent="red"].ptt-shell{--ptt-accent-lift:#e57373}


/* ==========================================================================
   2 · SHELL + TYPOGRAPHY
   ========================================================================== */
.ptt-shell{
	background:var(--ptt-surface);
	color:var(--ptt-copy);
	font-family:var(--ptt-body-font);
	font-size:var(--ptt-fs-body);
	line-height:1.55;
	/* 🔑 EVERY STRING ON THIS PAGE IS MERCHANT FREE TEXT, and this property is
	   INHERITED — so one declaration covers the business name, an offering
	   description, an address, a reservations email and a virtual location's
	   meeting URL alike, instead of a rule per text holder that the next new
	   field would miss.
	   ⚠️ `anywhere`, NEVER `break-word`: only `anywhere` also lets the
	   MIN-CONTENT size shrink, and the offenders here are flex items (cards,
	   contact rows, fact chips) whose min-content contribution is what drags
	   the container wide. `break-word` leaves that intrinsic floor in place and
	   the page still scrolls sideways. */
	overflow-wrap:anywhere;
}
/* ⚠️ Page-scoped, and NOT optional: newbookingpage.css sets `body{overflow:auto}`,
   which silently kills every position:sticky descendant, and the base sheet
   carries the same reset for the same reason. */
html,body{overflow:visible}

.ptt-shell .ptt-wrap{
	width:100%;
	max-width:var(--ptt-page);
	margin-inline:auto;
	padding-inline:var(--ptt-gutter);
}

/* 🔑 SEE § 0 RULE 5 — shell-prefixed (0,2,0) so it beats the base's
   `.pt-home-shell section{padding:0}` (0,1,1). A bare `.ptt-band{padding:…}`
   measures zero and the page renders as one unbroken column. */
.ptt-shell .ptt-band{padding:var(--ptt-band-pad) 0}
.ptt-shell .ptt-band-alt{background:var(--ptt-surface-2)}

.ptt-shell h1,.ptt-shell h2,.ptt-shell h3,.ptt-shell h4{
	font-family:var(--ptt-display);
	color:var(--ptt-title);
	margin:0;
}
.ptt-shell p{margin:0}

/* The display scale. Tracking tightens as the type grows — the ladder the base
   sheet documents, stated in `em` so the responsive blocks (which set the SIZE
   only) scale it for free. */
.ptt-shell .ptt-h1{
	font-size:var(--ptt-h1); font-weight:700; line-height:.98;
	letter-spacing:-.03em; text-transform:uppercase;
	color:var(--ptt-on-ink);
	/* Uppercasing in CSS, never with a JS toUpperCase(): locale-aware, and it
	   leaves the merchant's own casing in the DOM for the crawler and for the
	   clipped-title tooltip. */
}
/* 🔑 THE HERO HEADLINE IS THE BUSINESS NAME — TWO LINES, THEN AN ELLIPSIS, AND THE
   WHOLE NAME ON HOVER. That field is free merchant text at 64px, so uncapped it
   stops being a headline and becomes the hero: measured at 1440x900, a
   95-character name ran FOUR lines (250.88px) and pushed the hero CTA from
   y=350 to y=512 and the fact strip from y=438 to y=600 — i.e. off the first
   screen — on a hero that still overflows nothing and so reads as fine in
   review. Two lines is what an ordinary two- or three-word name already takes,
   so nothing about a real account moves.
   ⚠️ A CLAMP, not `text-overflow` — that only ever ellipsises a SINGLE line, so
   it cannot say "two lines, then …". It therefore overflows on the HEIGHT axis,
   which is the axis markClippedNames() tests alongside the width.
   ⚠️ 🔑 LEADING BELOW 1 MAKES A CLAMPED BOX REPORT ITSELF CLIPPED FOREVER, and
   the tooltip is what shows it. `line-height:.98` is TIGHTER than the font's own
   line box, so the glyphs' ink overflows it and `scrollHeight` runs past
   `clientHeight` even for a ONE-WORD headline — measured with Archivo loaded,
   2px at 64px/54px/42px/34px and 1px at 29px, i.e. a box the pass would call
   clipped at every size, standing the `title` permanently on a headline the
   visitor can read in full. That is the precise noise that pass exists to strip.
   ⚠️ The padding and the negative margin are ONE value and must move together:
   the padding gives the ink somewhere to sit INSIDE the box, and the margin
   takes exactly the same amount back so nothing on the page moves (the h1 has no
   bottom margin of its own to borrow from — `.ptt-shell h1` zeroes it). It works
   out identical whether or not the two siblings' margins collapse, because a
   collapsed pair of one positive and one negative margin SUMS.
   ⚠️ Stated in `em`, never px — the overflow is proportional to the font size and
   `--ptt-h1` is re-pointed FOUR times below (54/42/34/29px), so one `em` value
   covers all five instead of five px numbers to re-measure. .04em is where the
   residual first reaches 0 at every one of those sizes; .05em is that with one
   step of headroom, and it costs no layout (the margin returns it) and reveals
   no sliver of a third line (line-clamp does not render past the clamp).
   🔑 The pass's hook is this CLASS, added to CLIPPED_TEXT_SURFACES in
   bookingHomeTorqaView.js. It is deliberately NOT `.ptt-h1`: that is the display
   SCALE, and a second heading taking the scale must not silently inherit a
   two-line clamp and a tooltip with it. */
.ptt-shell .ptt-hero-title{
	display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2;
	overflow:hidden;
	padding-bottom:.05em; margin-bottom:-.05em;
}
.ptt-shell .ptt-h2{
	font-size:var(--ptt-h2); font-weight:700; line-height:1.14; letter-spacing:-.025em;
}
.ptt-shell .ptt-empty{
	padding:40px 0; text-align:center; color:var(--ptt-muted);
	font-size:var(--ptt-fs-body);
}

/* The reveal. That is the whole motion system: no parallax, no Ken Burns, and
   no hover that moves a card (a lift makes one tile in a row step out of line
   with its neighbours, and it moves the card out from under a pointer near its
   bottom edge, so hover flickers on and off). */
.ptt-shell .ptt-rv{
	opacity:0; transform:translateY(14px);
	transition:opacity .55s ease,transform .55s ease;
}
.ptt-shell .ptt-rv.ptt-in{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){
	.ptt-shell .ptt-rv{opacity:1;transform:none;transition:none}
}


/* ==========================================================================
   3 · BUTTONS — one geometry, four fills
   ⚠️ Every rule here is shell-prefixed; see § 0 rule 4. A bare `.ptt-btn` loses
   `font`, `font-size` and `color` to the base's own (0,1,1) element resets, and
   the label comes out in the body face at weight 400 — i.e. the rule that makes
   the CTAs part of the display system silently stops applying.
   ========================================================================== */
.ptt-shell .ptt-btn{
	display:inline-flex; align-items:center; justify-content:center; gap:8px;
	height:50px; padding:0 26px; border:1px solid transparent;
	border-radius:var(--ptt-r-pill);
	font-family:var(--ptt-display); font-weight:700; font-size:15px;
	line-height:1; letter-spacing:-.005em; white-space:nowrap;
	cursor:pointer;
	transition:background-color var(--ptt-move),border-color var(--ptt-move),color var(--ptt-move);
}
.ptt-shell .ptt-btn i{font-size:.85em;margin:0;width:auto}
.ptt-shell .ptt-btn-sm{height:40px;padding:0 18px;font-size:13.5px}
.ptt-shell .ptt-btn-block{width:100%}

/* ⚠️ The label colour is STATED, and it is the TOKEN: inherited it would be the
   body grey on the accent fill, and a literal #fff is unreadable on the yellow
   swatch (--pts-accent-on is dark there and white on the other nine). */
.ptt-shell .ptt-btn-primary{background:var(--ptt-accent);color:var(--pts-accent-on)}
.ptt-shell .ptt-btn-primary:hover{background:var(--ptt-accent-deep);color:var(--pts-accent-on)}

/* The outlined pill beside a section head, and the catalogue card's CTA. Teal
   as TYPE, so it reads --ptt-accent-ink (5.12:1), never the fill. */
.ptt-shell .ptt-btn-ghost{
	background:none; border-color:var(--ptt-accent-edge); color:var(--ptt-accent-ink);
	height:40px; padding:0 18px; font-size:13.5px;
}
.ptt-shell .ptt-btn-ghost:hover{
	background:var(--ptt-accent-soft); border-color:var(--ptt-accent); color:var(--ptt-accent-ink);
}

/* ⚠️ NO WHITE-ON-PHOTOGRAPH PILL. It was the hero's second CTA ("Browse
   availability"), which has been withdrawn — the hero states ONE action, Book
   now — and nothing else on this page sits a button on a dark ground, so the
   rule had no other caller. A dead one reads as a class somebody forgot to use
   and is what the next hero edit reaches for. If a dark band ever needs a
   secondary pill: rgba(255,255,255,.07) fill, .34 border, #fff type. */


/* ==========================================================================
   4 · HEADER — transparent, floating over the hero, NOT sticky
   The reference has it sitting ON the photograph. It is deliberately not
   sticky, which is also why the view wires no scroll spy: the nav is on screen
   only at the top of the document, where the first section is the active one
   anyway.
   ========================================================================== */
.ptt-shell .ptt-header{
	position:absolute; inset:0 0 auto; z-index:20;
	padding:22px 0;
}
.ptt-shell .ptt-header-in{
	width:100%; max-width:var(--ptt-page); margin-inline:auto;
	padding-inline:var(--ptt-gutter);
	display:flex; align-items:center; gap:24px;
}

.ptt-shell .ptt-brand{
	/* 🔑 The logo-to-wordmark gap is the BASE's `--pts-brand-gap` (6px), never a
	   value of this layout's own: the header and footer lockups are one Picktime
	   mark, and every other layout spaces it with this token. It was 10px here,
	   which made the same logo read as a looser, different lockup. A branded
	   header holds only the credit plate, so this changes nothing there. */
	display:inline-flex; align-items:center; gap:var(--pts-brand-gap);
	/* 🔑 CAPPED, and the cap is what keeps a long WHITE-LABELED business name
	   from taking the row. Flex hands a deficit out in proportion to each item's
	   basis, so an uncapped brand claims a share of the header proportional to
	   how long the name happens to be — and this row's other items (the nav
	   labels, the language chip, Log in, Book now) are fixed controls that
	   cannot compress. It is a CAP, never a min-width floor: a floor also
	   inflates a SHORT brand, and never a dominating flex-shrink, which starves
	   the name to a couple of letters and reads as a one-letter business.
	   🔑 AND NO `min-width:0` EITHER — that was here, and it is a different thing
	   from the flex-shrink the note above rules out. A flex item's automatic
	   minimum floors it at its own min-content; `min-width:0` removes that floor,
	   so this anchor was squeezed to 57.1px at 320px while the Picktime credit
	   chip inside it — a fixed pill with nothing to shrink and no text to
	   ellipsise — went on painting at its full 90.5px OUTSIDE the box, over the
	   Log in button beside it. Flex items never overlap; their CONTENT does, once
	   the box is smaller than the content.
	   ⚠️ It costs the white-labeled case nothing: `.ptt-brand-name` carries
	   `overflow:hidden`, which gives IT an automatic minimum of 0, so a long name
	   still ellipsises exactly as before — only the unshrinkable badge is
	   protected. Same fix, same reasoning, as Pawmi's `.ptp-header-inner` note. */
	max-width:260px;
	color:#fff;
}
.ptt-shell .ptt-brand-name{
	font-family:var(--ptt-display); font-weight:700; font-size:16px;
	letter-spacing:-.02em; color:#fff;
	overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0;
}
/* 🔑 THE ANCHOR'S CAP IS NOT ENOUGH ON A PHONE — the NAME carries a RELATIVE one too.
   `.ptt-brand`'s own `max-width:260px` (and the 150px it drops to further down) is the right
   DESKTOP answer and is why this layout was the least broken of the forks, but a px cap is
   only right at the width it was measured at: measured with a 100-character name, the header
   still ran **15px past a 320px viewport**, because 150px of brand plus this row's fixed
   controls is more than that screen holds.
   ⚠️ Capped on the NAME rather than by lowering the anchor further: the anchor also draws the
   credit plate (see its own note), and a tighter anchor cap would squeeze that too.
   ⚠️ Scoped to `.ptt-header` because the FOOTER lockup reuses this class, where the brand
   block is full width and needs no cap of its own.
   Measured after, long name, signed in and out, 320→2560px: zero page overflow at every width,
   name always ellipsised, and never worse than the short-name baseline. */
.ptt-shell .ptt-header .ptt-brand-name{ max-width:min(260px, max(88px, 17vw)) }
.ptt-shell .ptt-mark{
	flex:0 0 auto;
	width:36px; height:36px; border-radius:var(--ptt-r-sm);
	display:inline-flex; align-items:center; justify-content:center;
	overflow:hidden;
	background:var(--ptt-accent); color:var(--pts-accent-on);
	font-family:var(--ptt-display); font-weight:700; font-size:14px; line-height:1;
}
/* ⚠️ Pinned explicitly — newbookingpage.css leaks a bare `img{width:100%}`,
   which stretches any unpinned image across its box. */
.ptt-shell .ptt-mark img{width:100%;height:100%;object-fit:cover;display:block}
/* 🔑 NO PADDING — the Picktime mark fills its box edge to edge, exactly as the base's
   .pts-footer-mark draws it. A 3px white plate was here from the ink-footer days; on
   the white footer that plate is invisible, so all it did was shrink the logo to 30px
   and push it 3px further from the wordmark, turning the shared 6px --pts-brand-gap
   into a visible 9px. The PNG carries its own rounded plate, so nothing is lost. */
.ptt-shell .ptt-mark-pt{background:transparent;padding:0}

.ptt-shell .ptt-nav{
	--ptt-nav-h:21px;
	display:flex; align-items:center; gap:26px;
	/* The nav is the one item that YIELDS: every entry it holds is also in the
	   footer's Explore/Offerings columns, while the brand, the account chip and
	   Book now must stay whole. min-width:0 + overflow:hidden is the safety net
	   — losing the last entry is a far better failure than pushing the actions
	   off the page.
	   🔑 BUT IT HAS TO LOSE THAT ENTRY WHOLE, WHICH IS WHY IT WRAPS. `overflow:hidden`
	   on a nowrap row cuts wherever the box ends, and a word sliced down the middle
	   reads as a rendering fault rather than as a shorter nav — measured at 1280 /
	   1440 / 1920, "FAQs" rendered at 79% of its width, i.e. as a label a visitor
	   takes for the real one. `flex-wrap:wrap` + `align-content:flex-start` + a
	   ONE-ROW height sends the entry that does not fit to a second row the height
	   clips away entirely. Same mechanism as the base sheet's `.pts-header-nav`,
	   which is why Dentara — which inherits that rule rather than restating it —
	   drops theirs cleanly at the same width.
	   ⚠️ The row's height and the entry's height are ONE token — a hard-coded height
	   drifts from the link's own line box, and a nav one pixel taller than its row
	   shows a strip of the clipped row beneath it. */
	flex-wrap:wrap; align-content:flex-start; height:var(--ptt-nav-h);
	min-width:0; overflow:hidden;
	margin-left:auto;
}
.ptt-shell .ptt-nav a{
	font-family:var(--ptt-body-font); font-size:var(--ptt-fs-meta); font-weight:500;
	height:var(--ptt-nav-h); display:inline-flex; align-items:center; flex:0 0 auto;
	color:rgba(255,255,255,.82); white-space:nowrap;
	transition:color var(--ptt-move);
}
.ptt-shell .ptt-nav a:hover{color:#fff}

.ptt-shell .ptt-actions{
	display:flex; align-items:center; gap:10px;
	margin-left:auto; flex:0 0 auto;
}
/* When the nav is hidden (≤1140px) the nav's own auto margin disappears with
   it, so the actions keep their own — otherwise the whole row hugs the left and
   reads as a half-loaded header. */


/* ==========================================================================
   5 · HERO — the photograph IS the section
   ========================================================================== */
.ptt-shell .ptt-hero{
	position:relative; isolation:isolate;
	display:flex; align-items:flex-end;
	min-height:640px;
	padding:150px 0 150px;
	background:var(--ptt-ink);
	overflow:hidden;
}
.ptt-shell .ptt-hero-media{
	position:absolute; inset:0; z-index:-1;
	margin:0; max-width:none;
}
/* ⚠️ Both dimensions AND object-fit are stated: newbookingpage.css's bare
   `img{width:100%}` sets the width but not the height, so an un-pinned banner
   renders at its natural height and leaves the rest of the hero bare ink. */
.ptt-shell .ptt-hero-media img{
	width:100%; height:100%; object-fit:cover; display:block;
}
/* 🔑 THE SCRIM IS THIS LAYOUT'S OWN ELEMENT, not the base's `.pts-cover::after`.
   Here the headline sits ON the photograph, so the darkening is load-bearing
   for legibility rather than decorative — it has to be measurable and tunable
   independently, and the base's gradient is neutralised in § 20.
   Two stops, both rgba() over the ink triplet: a flat wash would either be too
   light under the headline or too heavy over the picture. */
.ptt-shell .ptt-hero-scrim{
	position:absolute; inset:0;
	background:linear-gradient(90deg,rgba(9,10,12,.92) 0%,rgba(9,10,12,.78) 42%,rgba(9,10,12,.42) 100%);
}

.ptt-shell .ptt-hero-in{
	position:relative; z-index:1;
	width:100%; max-width:var(--ptt-page); margin-inline:auto;
	padding-inline:var(--ptt-gutter);
}
/* ⚠️ SCOPED TO THE HERO, and not for specificity — the rule paints
   --ptt-accent-lift, which is legal ONLY on a dark ground (2.59:1 on white), so
   the selector has to say which ground it is on. Unscoped it reads as a
   page-wide eyebrow style that happens to be used on the hero, and the next
   eyebrow added to a light band inherits an unreadable colour. */
.ptt-shell .ptt-hero .ptt-eyebrow{
	display:flex; flex-wrap:wrap; align-items:center; gap:10px;
	margin-bottom:18px;
	font-family:var(--ptt-body-font); font-size:var(--ptt-fs-cap); font-weight:600;
	letter-spacing:.14em; text-transform:uppercase;
	/* Teal as TYPE on ink ⇒ the LIFTED value (7.37:1). The fill would be
	   3.98:1 here — see § 0a. */
	color:var(--ptt-accent-lift);
}
.ptt-shell .ptt-hero .ptt-eyebrow span + span::before{
	content:''; display:inline-block; vertical-align:middle;
	width:4px; height:4px; margin-right:10px; border-radius:50%;
	background:currentColor; opacity:.55;
}
.ptt-shell .ptt-hero-lead{
	max-width:560px; margin-top:20px;
	font-size:16px; color:rgba(255,255,255,.82);
}
.ptt-shell .ptt-hero-cta{
	display:flex; flex-wrap:wrap; gap:12px;
	margin-top:30px;
}

/* The fact chips. --ptt-fact-basis is re-pointed per COUNT below, because the
   base emits 3 or 4 depending on the account and a fixed basis leaves a
   visibly empty cell. */
.ptt-shell .ptt-hero-facts{
	display:flex; flex-wrap:wrap; gap:10px 28px;
	margin:38px 0 0; padding:26px 0 0; list-style:none;
	border-top:1px solid rgba(255,255,255,.16);
}
.ptt-shell .ptt-hero-facts li{
	display:flex; align-items:center; gap:10px; min-width:0;
	font-size:var(--ptt-fs-meta); font-weight:500;
	color:rgba(255,255,255,.88);
}
.ptt-shell .ptt-hero-facts li span{
	min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.ptt-shell .ptt-hero-facts i{
	flex:0 0 auto; font-size:16px; margin:0; width:auto;
	color:var(--ptt-accent-lift);
}


/* ==========================================================================
   6 · IDENTITY — lifted onto the hero's bottom edge
   ========================================================================== */
.ptt-shell .ptt-identity{
	position:relative; z-index:3;
	margin-top:-92px;
}
.ptt-shell .ptt-id-row{
	display:flex; flex-wrap:wrap; gap:var(--ptt-gap);
	align-items:stretch;
}
.ptt-shell .ptt-card{
	background:var(--ptt-surface);
	border:1px solid var(--ptt-line);
	border-radius:var(--ptt-r-lg);
	min-width:0;
}
.ptt-shell .ptt-id-card{
	flex:1 1 0; min-width:0;
	padding:26px 28px;
	box-shadow:0 18px 44px rgba(14,16,19,.10);
}
.ptt-shell .ptt-start-card{
	/* ⚠️ `0 1`, never `1 1` — this panel is a fixed-width sidebar and a growable
	   one takes the whole row when it wraps onto a line of its own. */
	flex:0 1 330px; min-width:0;
	padding:24px 26px;
	display:flex; flex-direction:column;
	box-shadow:0 18px 44px rgba(14,16,19,.10);
}

.ptt-shell .ptt-id-head{display:flex;align-items:flex-start;gap:16px}
.ptt-shell .ptt-id-mark{
	flex:0 0 auto;
	width:72px; height:72px; border-radius:14px; overflow:hidden;
	display:inline-flex; align-items:center; justify-content:center;
	background:var(--ptt-accent); color:var(--pts-accent-on);
	font-family:var(--ptt-display); font-weight:700; font-size:24px; line-height:1;
}
.ptt-shell .ptt-id-mark img{width:100%;height:100%;object-fit:cover;display:block}
.ptt-shell .ptt-id-txt{min-width:0}
.ptt-shell .ptt-id-name{
	font-size:22px; font-weight:700; letter-spacing:-.025em; line-height:1.2;
	/* One line, ellipsised — the full text is the `title` and the clipped-title
	   pass keeps it only while it is really cut off. */
	overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.ptt-shell .ptt-id-industry{
	margin-top:4px; font-size:var(--ptt-fs-meta); color:var(--ptt-muted);
}
/* The strip renderRating() and renderOpenState() write into — the star rating,
   then "Open now" / "Closed" and the closing time beside it. Only the spacing is
   this layout's: the chips themselves are painted by the base's own `.pts-meta`
   rules, off `--pts-success` / `--pts-danger`, which § 0 already re-points onto
   this palette — so a chip the base adds LATER wears these colours for free.
   ⚠️ `:empty` is not tidiness. Both patchers are async (a working-hours fetch and
   a reviews fetch) and either can legitimately never land — reviews switched off,
   business hours hidden, a business with no reviews yet — and an empty flex box
   still carries its own margins, which would leave a gap under the industries
   line on exactly those accounts. */
.ptt-shell .ptt-id-meta{
	margin:10px 0 0; gap:6px 14px;
	font-size:var(--ptt-fs-meta); color:var(--ptt-muted);
}
.ptt-shell .ptt-id-meta:empty{display:none}
.ptt-shell .ptt-id-rows{
	margin-top:20px; padding-top:18px; border-top:1px solid var(--ptt-line);
	display:flex; flex-direction:column; gap:10px;
}
.ptt-shell .ptt-id-row-item{
	display:flex; align-items:flex-start; gap:10px; min-width:0;
	font-size:var(--ptt-fs-meta); color:var(--ptt-copy);
}
.ptt-shell .ptt-id-row-item i{
	flex:0 0 auto; margin:0; width:auto; font-size:14px; line-height:1.5;
	color:var(--ptt-accent-ink);
}
.ptt-shell .ptt-id-row-item span{min-width:0}
.ptt-shell .ptt-id-row-item a{color:var(--ptt-copy);font-size:inherit}
.ptt-shell .ptt-id-row-item a:hover{color:var(--ptt-accent-ink)}

.ptt-shell .ptt-socials{display:flex;flex-wrap:wrap;gap:8px;margin-top:18px}
.ptt-shell .ptt-social{
	width:34px; height:34px; border-radius:50%;
	display:inline-flex; align-items:center; justify-content:center;
	border:1px solid var(--ptt-line); color:var(--ptt-copy);
	transition:border-color var(--ptt-move),color var(--ptt-move),background-color var(--ptt-move);
}
.ptt-shell .ptt-social i{margin:0;width:auto;font-size:14px;line-height:1}
.ptt-shell .ptt-social:hover{
	background:var(--ptt-accent-soft); border-color:var(--ptt-accent-edge); color:var(--ptt-accent-ink);
}

/* --- the Share control, the last item of that same row ---------------------
   It wears `.ptt-social` so it reads as one more control of the row rather
   than a foreign pill, which means it inherits that rule's 34px box — and the
   base's own `.pt-home-shell .pts-share-btn` is (0,2,0), EXACTLY the weight of
   `.ptt-shell .ptt-social`, so which one wins per property is decided by
   source order alone. This sheet loads after the base, so `.ptt-social` takes
   the box; what it never states is `padding` and `background`, so the base's
   `padding:0 12px` and `background:var(--pts-surface)` survive into a 34px
   border-box and squeeze the glyph into 10px. ⚠️ Both must therefore be reset
   HERE, explicitly — this is not a redundant restatement of the icon rule.
   The popover itself is deliberately unstyled: the base draws it from the
   --pts-* palette, which § 3 re-points onto Torqa's own surface/line/title, so
   it already wears this page's colours and a second treatment would only drift
   from the one the seven designs ship. */
.ptt-shell .ptt-share-btn{
	padding:0; background:transparent;
	font-size:inherit; font-weight:inherit;
	min-height:0;   /* the base sets 34px as a MIN; .ptt-social's height is exact */
	cursor:pointer;
}
.ptt-shell .ptt-share-btn:hover{
	background:var(--ptt-accent-soft); border-color:var(--ptt-accent-edge); color:var(--ptt-accent-ink);
}
/* ⚠️ The row is `flex-wrap:wrap`, so the open popover must not be clipped by a
   line box: `.pts-share` is the positioned ancestor the base anchors the menu
   to, and nothing between it and `.ptt-card` sets `overflow`, so it escapes the
   card. Stated here only to keep the item from stretching to the row's height
   (`.ptt-socials` has no `align-items`, so the default `stretch` would make the
   34px button a full-height box with the menu hanging off its bottom). */
.ptt-shell .ptt-socials > .pts-share{align-self:center}

.ptt-shell .ptt-start-title{
	font-size:var(--ptt-fs-cap); font-weight:700; letter-spacing:.1em;
	text-transform:uppercase; color:var(--ptt-muted);
}
.ptt-shell .ptt-start-row{
	display:flex; align-items:center; gap:10px; min-width:0;
	margin-top:14px; padding-bottom:14px; border-bottom:1px solid var(--ptt-line);
	font-size:var(--ptt-fs-meta); color:var(--ptt-copy);
}
.ptt-shell .ptt-start-row i{flex:0 0 auto;margin:0;width:auto;font-size:14px;color:var(--ptt-accent-ink)}
.ptt-shell .ptt-dot{
	flex:0 0 auto; width:8px; height:8px; border-radius:50%; background:var(--ptt-muted);
}
.ptt-shell .ptt-dot.is-open{background:var(--ptt-success)}
.ptt-shell .ptt-dot.is-closed{background:var(--ptt-danger)}
.ptt-shell .ptt-start-note{
	margin-top:14px; font-size:var(--ptt-fs-cap); color:var(--ptt-muted);
}
/* The CTA takes the panel's free space, so the two cards' bottom edges line up
   whatever the identity card's height turns out to be. */
.ptt-shell .ptt-start-card .ptt-btn{margin-top:auto}
.ptt-shell .ptt-start-note + .ptt-btn{margin-top:18px}


/* ==========================================================================
   7 · SECTION HEAD — the title over a short accent rule, action opposite
   ========================================================================== */
.ptt-shell .ptt-head{
	display:flex; align-items:flex-end; justify-content:space-between; gap:20px;
	margin-bottom:34px;
}
/* ⚠️ The text block is the shrinkable one and the action stays whole — a
   section head must never stack, or its action lands at the left edge under the
   heading where it reads as a stray line of copy rather than the section's
   action. */
.ptt-shell .ptt-head-txt{min-width:0}
.ptt-shell .ptt-head > .ptt-btn{flex:0 0 auto}
/* A head can carry MORE THAN ONE action — the reviews section has both "View all"
   and "Leave Review". They sit on one line rather than stacking, which is what
   they did when the second rode the filter-chip row below (a lone right-aligned
   pill directly under the first, with an empty band around it).
   ⚠️ The heights are matched explicitly: the shared `.pts-btn-small` is the base's
   control and is a different size from this layout's own ghost pill, so left alone
   the two sit at different heights in the same row. */
.ptt-shell .ptt-head-actions{display:flex;align-items:center;gap:10px;flex:0 0 auto}
.ptt-shell .ptt-head-actions .pts-btn-small{
	height:40px; padding:0 18px; border-radius:var(--ptt-r-pill);
	font-family:var(--ptt-display); font-weight:700; font-size:13.5px; line-height:1;
}
.ptt-shell .ptt-rule{
	display:block; width:54px; height:4px; margin-top:14px; border-radius:2px;
	background:var(--ptt-accent);
}


/* ==========================================================================
   8 · CATEGORIES — one per ROW, each headed by its own name
   🔑 EVERY CATEGORY IS ON THE PAGE AT ONCE. This layout used to present them as
   a filter row ("All · Strength · Cardio") that showed one block at a time; the
   chips, their `.ptt-cat-off` hide rule and the binder behind them are all gone.
   A category is now simply a labelled row with its own rail, which is how the
   seven present one (a stacked block per category) and what makes the merchant's
   whole catalogue readable by scrolling rather than by pressing.
   ⚠️ THE HEADING IS NOT OPTIONAL CHROME — with the chips gone it is the only
   thing naming a row. Under the old default ("All" pressed) the blocks stacked
   with their headings SUPPRESSED, so two categories read as two unlabelled rails
   30px apart; that is the state this replaces, and dropping the heading again
   restores it exactly.
   ========================================================================== */
.ptt-shell .ptt-cat + .ptt-cat{margin-top:30px}
/* The row's name on the left, its two rail arrows on the right. The 16px gap
   below it belongs to the ROW, not to the heading — the heading is optional and
   the arrows are not, so a margin on the heading leaves an unheaded category's
   arrows sitting on its cards. */
.ptt-shell .ptt-cat-head{
	display:flex; align-items:center; gap:16px;
	margin-bottom:16px; min-height:36px;
}
/* ⚠️ The NAV is what anchors right, never the row's `justify-content` — with no
   heading beside it, a space-between row puts its only item on the LEFT. */
.ptt-shell .ptt-cat-nav{display:flex;align-items:center;gap:8px;margin-left:auto}
/* Nothing to page ⇒ no arrows, for the same reason `.ptt-dots:empty` draws
   nothing: two permanently dead circles state something untrue about the row.
   Both are decided by the same `pages` number in syncRail, so they cannot
   disagree. */
.ptt-shell .ptt-cat:not(.ptt-can-page) .ptt-cat-nav{display:none}
/* …and with no heading either, the row would hold nothing at all — so it goes.
   ⚠️ A CLASS the template sets, not a `:has()` test: this sheet already refuses
   CSS Grid on compatibility grounds and `:has()` is newer still. */
.ptt-shell .ptt-cat:not(.ptt-can-page) .ptt-cat-head-bare{display:none}
.ptt-shell .ptt-cat-name{
	margin:0; min-width:0;
	font-size:var(--ptt-fs-meta); font-weight:700; letter-spacing:.07em;
	text-transform:uppercase; color:var(--ptt-muted);
}
.ptt-shell .ptt-cat-name span{
	margin-left:8px; padding:2px 8px; border-radius:var(--ptt-r-pill);
	background:var(--ptt-accent-soft); color:var(--ptt-accent-ink);
	font-size:var(--ptt-fs-micro); letter-spacing:0;
}


/* ==========================================================================
   9 · CATALOGUE CARDS — one shape for services, classes and resources
   ========================================================================== */
/* 🔑 THE CATALOGUE IS A HORIZONTAL SCROLLER, ONE ROW PER CATEGORY — the
   reference's carousel. `--ptt-card-basis` is solved from the gap so exactly N
   cards fill the scroller's width, which is what makes a page-width scroll land
   on a card boundary; the responsive blocks re-point it to 3-, 2- and 1-up.
   ⚠️ The scrollbar is hidden, so the arrows are NOT optional: without them a
   mouse-only visitor has no way to reach the cards past the fold (a vertical
   wheel scrolls the page, not the row). The dots are the second affordance and
   they PAGE the rail — see bindRails(). */
/* ⚠️ `.ptt-rail` states NOTHING. Its `position:relative` existed only to be the
   containing block for the two arrows that floated over the scroller; they are
   in the category's head row now, and nothing else inside the rail is out of
   flow. It survives as a box because syncRail iterates it and the dots are its
   second child. */
.ptt-shell .ptt-cards{
	display:flex; gap:var(--ptt-gap);
	overflow-x:auto; overflow-y:hidden;
	scroll-snap-type:x mandatory;
	/* A visible bar under a row of cards reads as a broken panel — the same call
	   the base makes for its pill row, and the reason the arrows exist. */
	scrollbar-width:none; -ms-overflow-style:none;
	/* Room for the card's hover shadow, which a clipped scroller would cut off. */
	padding-bottom:6px;
}
.ptt-shell .ptt-cards::-webkit-scrollbar{display:none}
.ptt-shell .ptt-item{
	/* ⚠️ `0 0` HERE, and that is not the wrapping-row doctrine being broken: this
	   is a SCROLLER, so a card must neither grow nor shrink or the row would fit
	   itself to the viewport instead of overflowing it, and there would be
	   nothing to scroll. The team grid below is a wrapping row and keeps `0 1`. */
	flex:0 0 var(--ptt-card-basis); min-width:0;
	scroll-snap-align:start;
	position:relative;
	display:flex; flex-direction:column;
	overflow:hidden;
	transition:border-color var(--ptt-move),box-shadow var(--ptt-move);
}
/* No transform on hover — see § 2's note on the reveal. */
.ptt-shell .ptt-item:hover{
	border-color:var(--ptt-accent-edge);
	box-shadow:0 12px 30px rgba(14,16,19,.08);
}
.ptt-shell .ptt-thumb{
	position:relative; aspect-ratio:16/11; background:var(--ptt-surface-2);
	display:flex; align-items:center; justify-content:center;
	overflow:hidden;
}
.ptt-shell .ptt-thumb img{width:100%;height:100%;object-fit:cover;display:block}
/* 🔑 THE FALLBACK IS NOT AN EDGE CASE — the reference's own service cards are
   glyphs rather than photographs, so an account whose services carry no image
   is the COMMON case. Without a box the card keeps its title at the top and its
   CTA at the bottom with a void between them, while the card beside it looks
   finished. */
.ptt-shell .ptt-thumb-badge{
	background:var(--ptt-accent-soft);
}
.ptt-shell .ptt-thumb-badge span{
	font-family:var(--ptt-display); font-weight:700; font-size:26px; line-height:1;
	letter-spacing:-.02em; color:var(--ptt-accent-ink);
}

/* 🔑 A FLEX COLUMN WITH A `gap`, and that gap is what guarantees the air above
   the CTA. The CTA takes `margin-top:auto` so the labels line up across a row,
   but an auto margin collapses to ZERO on the tallest card in that row — so
   without a gap the button would touch the price on exactly the card a visitor
   is most likely to be reading. The gap is the floor; auto adds the rest.

   ⚠️ `flex:1 1 auto` IS WHAT GIVES THAT AUTO MARGIN ANYTHING TO SPEND. The card
   is the flex item that stretches to the tallest in its line, but the BODY
   inside it is a second flex item that sizes to its own content — so without
   this the card's free space pooled BELOW the body, `margin-top:auto` resolved
   against a shrink-wrapped box and every CTA sat directly under its own copy:
   measured with the reported catalogue, "Book now" landed at four different
   heights across one row and a card with no description left ~90px of bare
   ground beneath its button. The body must take the slack for the CTA to reach
   the card's floor. */
.ptt-shell .ptt-item-body{
	flex:1 1 auto;
	display:flex; flex-direction:column; gap:10px;
	padding:18px 18px 20px; min-width:0;
}
.ptt-shell .ptt-item-name{
	font-size:var(--ptt-h3); font-weight:700; letter-spacing:-.015em; line-height:1.3;
	/* One line, ellipsised; the full text rides on .pts-name as a `title` and
	   the base's clipped-title pass strips it wherever nothing is hidden.
	   ⚠️ Raised above the card-wide link overlay and made click-through, with
	   pointer-events handed back to the name itself: the heading's line box is
	   the full width of the card, so a lifted heading would otherwise steal the
	   click from every empty pixel beside a short name. */
	position:relative; z-index:2; pointer-events:none;
	overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* ⚠️ `font:inherit` is not optional — newbookingpage.css leaks a bare
   `a{font-weight:400;font-size:16px}` and the shell's own anchor reset covers
   the colour and the size but NOT the weight, so wrapping the heading's text in
   an anchor drops every card title from 700 to 400 and the whole catalogue
   stops being bold. */
.ptt-shell .ptt-item-name .pts-name{
	font:inherit; letter-spacing:inherit; color:inherit;
	pointer-events:auto;
}
/* No margins on these — the body's own `gap` owns the rhythm, so a card that
   renders only some of them (SHOW PRICE / SHOW DURATION can each empty their
   half) cannot end up with a doubled or a missing step. */
.ptt-shell .ptt-item-meta{
	font-size:var(--ptt-fs-meta); color:var(--ptt-muted);
}
.ptt-shell .ptt-item-price{
	font-family:var(--ptt-display); font-weight:700; font-size:16px; letter-spacing:-.01em;
	color:var(--ptt-title);
}
.ptt-shell .ptt-item-price span{
	margin-left:6px; font-family:var(--ptt-body-font); font-weight:400;
	font-size:var(--ptt-fs-micro); color:var(--ptt-muted);
}
/* The outlined Book-now every catalogue row carries. A <span>, click-through —
   the overlay IS the link and carries the accessible name, so an <a> here would
   take the click off it and announce the card twice — and raised only so it
   paints above that overlay. */
.ptt-shell .ptt-item-cta{
	position:relative; z-index:1; pointer-events:none;
	display:flex; align-items:center; justify-content:center;
	/* ⚠️ `margin-top:auto` is what spends the card's free space, so the CTAs
	   across a row line up whatever each card's copy turns out to be — and the
	   MINIMUM air above it is the body's `gap`, never a second margin here: a
	   `margin-top:18px` written after this would simply replace the auto and the
	   labels would stop lining up. */
	margin-top:auto; height:40px;
	border:1px solid var(--ptt-accent-edge); border-radius:var(--ptt-r-pill);
	font-family:var(--ptt-display); font-weight:700; font-size:13.5px; line-height:1;
	color:var(--ptt-accent-ink);
	transition:background-color var(--ptt-move),color var(--ptt-move),border-color var(--ptt-move);
}
.ptt-shell .ptt-item:hover .ptt-item-cta{
	background:var(--ptt-accent); border-color:var(--ptt-accent); color:var(--pts-accent-on);
}
.ptt-shell .ptt-item-link{position:absolute;inset:0;z-index:0}

/* ---- the rail's arrows, in the category's HEAD ROW ---------------------
   🔑 THEY SIT IN THE HEAD ROW, NOT OVER THE CARDS. They used to be absolutely
   positioned against the scroller's left and right edges and vertically centred
   from JS, which at 4-up put a 42px circle ON TOP OF the first and last cards
   and covered the start of one name and the end of the other. Static in the
   head row they cover nothing, they need no measuring, and they sit beside the
   heading of the row they page. Consequences that are easy to undo by accident:
   the pair is laid out by `.ptt-cat-nav` above, so NO `position`/`top`/`left`/
   `right`/`transform` belongs here; the drop shadow went with the floating
   position (chrome for a circle over a photograph, noise on the page ground);
   and the three state classes ride `.ptt-cat`, because the head row is the
   RAIL'S SIBLING and a class on the rail cannot reach it. */
.ptt-shell .ptt-rail-arrow{
	flex:0 0 auto;
	display:flex; align-items:center; justify-content:center;
	width:36px; height:36px; border-radius:50%;
	background:var(--ptt-surface); border:1px solid var(--ptt-line);
	color:var(--ptt-title);
	transition:background-color var(--ptt-move),color var(--ptt-move),
			border-color var(--ptt-move),opacity var(--ptt-move);
}
.ptt-shell .ptt-rail-arrow i{margin:0;width:auto;font-size:13px;line-height:1}
.ptt-shell .ptt-rail-arrow:hover{background:var(--ptt-accent);border-color:var(--ptt-accent);color:var(--pts-accent-on)}
/* 🔑 DISABLED, NEVER HIDDEN — the whole point of the pair being in a row.
   Hiding one moves the other, so the first scroll of every rail would shift its
   own controls sideways under the pointer that just clicked them. (Floating
   over the cards they could be hidden freely: they were out of flow.)
   ⚠️ `pointer-events:none` is what makes the state real rather than painted;
   the `:hover` rule above would otherwise still light a dead arrow. */
.ptt-shell .ptt-cat:not(.ptt-can-left) .ptt-rail-prev,
.ptt-shell .ptt-cat:not(.ptt-can-right) .ptt-rail-next{
	opacity:.35; color:var(--ptt-muted); pointer-events:none;
}
/* 🔑 SHOWN AT EVERY WIDTH, and the narrow end is the one that needs them MOST
   (see `.ptt-cat-nav`, which is where the pair is hidden when there is nothing
   to page at all). This was once gated to `min-width:921px`, on the reasoning
   that a narrow row is swiped and a touch scroll is its own affordance — but
   the gate is on the VIEWPORT, not on the input, so a narrow desktop window
   (and every phone in a desktop browser's device emulation) lost the arrows
   while still having no bar to drag: the scrollbar is hidden, a vertical wheel
   scrolls the page, and the row below 1-up carries MORE pages than at 4-up, so
   the cards past the fold were unreachable with a pointer. A touch visitor
   simply keeps swiping and the arrows cost them nothing. */

.ptt-shell .ptt-dots{
	display:flex; justify-content:center; align-items:center; gap:8px;
	margin-top:18px;
}
/* Nothing to page ⇒ syncRail empties it, and an empty row must not reserve
   space above the next section. */
.ptt-shell .ptt-dots:empty{display:none}
.ptt-shell .ptt-dot{
	width:8px; height:8px; padding:0; border:0; border-radius:50%;
	background:var(--ptt-line); cursor:pointer;
	transition:background-color var(--ptt-move),width var(--ptt-move);
}
.ptt-shell .ptt-dot-on{width:22px;border-radius:4px;background:var(--ptt-accent)}

/* 🔑 THE TEAM'S PREVIEW LIMIT NEEDS ITS OWN RULE. The base hides overflow items
   with `.pts-tiles[data-pts-collapsed] .pts-overflow` / `.pts-rows[…]` — class
   names this layout's container does not carry — so without this the team's
   "Load more" renders above a grid that is already showing everyone.
   ⚠️ THE CATALOGUE HAS NO COUNTERPART, deliberately: its category is a rail and
   opts out of the collapse entirely (bookingHomeTorqaView.applyPreviewLimit), so
   `.ptt-cards` never carries the attribute and a rule for it would be dead.
   ⚠️ Match the VALUE ="true", never the bare attribute: the base's Load more
   handler EXPANDS by setting data-pts-collapsed="false" (it does not remove
   it), so a presence selector keeps the overflow hidden after the click. */
.ptt-shell .ptt-team[data-pts-collapsed="true"] .pts-overflow{display:none}


/* ==========================================================================
   10 · ABOUT
   ⚠️ No image and no stat strip here — see the template header. The only real
   photograph available is the gallery's, and using it here prints the same
   picture twice on one page; "years trading" does not exist on AccountJDO.
   ========================================================================== */
.ptt-shell .ptt-about{display:flex;flex-wrap:wrap;gap:36px;align-items:flex-start}
.ptt-shell .ptt-prose{flex:1 1 0;min-width:0}
.ptt-shell .ptt-prose p{
	font-size:16px; line-height:1.7; color:var(--ptt-copy);
	/* The base's aboutParagraphs() keeps a single line break inside a paragraph,
	   so it has to survive on a page that escapes what it renders. */
	white-space:pre-line;
}
.ptt-shell .ptt-prose p + p{margin-top:16px}
.ptt-shell .ptt-details{
	flex:0 1 340px; min-width:0;
	padding:26px 28px;
	display:flex; flex-direction:column; gap:18px;
}
.ptt-shell .ptt-detail{display:flex;align-items:flex-start;gap:12px;min-width:0}
.ptt-shell .ptt-tick{
	flex:0 0 auto; width:26px; height:26px; border-radius:50%;
	display:inline-flex; align-items:center; justify-content:center;
	background:var(--ptt-accent-soft); color:var(--ptt-accent-ink);
}
.ptt-shell .ptt-tick i{margin:0;width:auto;font-size:11px;line-height:1}
.ptt-shell .ptt-detail-txt{min-width:0;display:flex;flex-direction:column}
.ptt-shell .ptt-detail-txt strong{
	font-family:var(--ptt-display); font-weight:700; font-size:var(--ptt-fs-body);
	letter-spacing:-.01em; color:var(--ptt-title);
}
.ptt-shell .ptt-detail-txt span{font-size:var(--ptt-fs-cap);color:var(--ptt-muted)}


/* ==========================================================================
   11 · TEAM
   ========================================================================== */
.ptt-shell .ptt-team{display:flex;flex-wrap:wrap;gap:var(--ptt-gap)}
.ptt-shell .ptt-member{
	flex:0 1 var(--ptt-card-basis); min-width:0;
	position:relative;
	display:flex; flex-direction:column; align-items:center; text-align:center;
	padding:28px 20px 26px;
	/* 🔑 A FIXED HEIGHT, and it cannot become `auto`. A team NAME is clamped at
	   TWO lines, so one that wraps makes its card a line taller than its
	   neighbours' — and a min-height (or the flex line's own stretch) equalises
	   only the cards that SHARE a line, so with the longest name on row 1 the
	   rows under it come out shorter. The height is a CEILING the content can
	   never exceed (name 2 lines, designation 1), so nothing is ever clipped. */
	/* ⚠️ 268 → 232 WHEN THE PER-MEMBER LINK WAS WITHDRAWN (see the template): the
	   height is a DERIVED SUM — padding 28 + avatar 92 + 16 + a two-line name +
	   the designation line + padding 26 — and the link was a term of it. Left at
	   268 every card carried the ~37px the link used to occupy as dead ground
	   under the designation, which is the void the seven designs' own team cards
	   showed for a member with no link. Measured: the card's natural ceiling
	   (two-line name + designation) is 231.3px. */
	height:var(--ptt-team-h,232px);
	transition:border-color var(--ptt-move),box-shadow var(--ptt-move);
}
/* NOBODY has a designation ⇒ the derived sum loses that term too. ⚠️ Re-derive
   BOTH values if any term moves; this is 232 minus the designation line and its
   gap (24px, measured), and restoring the link is +37 on each. */
.ptt-shell .ptt-team[data-ptt-team-lean="true"] .ptt-member{--ptt-team-h:208px}
.ptt-shell .ptt-member:hover{
	border-color:var(--ptt-accent-edge); box-shadow:0 12px 30px rgba(14,16,19,.08);
}
.ptt-shell .ptt-avatar{
	width:92px; height:92px; border-radius:50%; overflow:hidden;
	display:inline-flex; align-items:center; justify-content:center;
	background:var(--ptt-accent-soft); color:var(--ptt-accent-ink);
	font-family:var(--ptt-display); font-weight:700; font-size:26px; line-height:1;
}
.ptt-shell .ptt-avatar img{width:100%;height:100%;object-fit:cover;display:block}
.ptt-shell .ptt-member-name{
	margin-top:16px; min-width:0; width:100%;
	font-size:var(--ptt-h3); font-weight:700; letter-spacing:-.015em; line-height:1.3;
	/* ⚠️ A CLAMP, not text-overflow — that only ever ellipsises a SINGLE line, so
	   it cannot express "two lines, then …". It therefore overflows on the
	   HEIGHT axis, which is why the base's clipped-title pass tests both. */
	display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; line-clamp:2;
	overflow:hidden;
}
.ptt-shell .ptt-member-role{
	margin-top:3px; width:100%; min-width:0;
	font-size:var(--ptt-fs-meta); color:var(--ptt-muted);
	/* ⚠️ Carries NEITHER .pts-tile-desc NOR .pts-clamp in the markup: those two
	   classes ARE what markClampedText() measures, so a designation with them
	   would sprout a Read More button and cost that one card a line of its own —
	   re-creating the very height difference the fixed height removes. */
	overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* The per-member link's rule, kept beside its commented-out markup so the two
   are restored together — and dead while that markup is. ⚠️ It is `margin-top:
   auto`, so it needs the card's fixed height to have the link's term back in it
   (see --ptt-team-h above) or the labels stop lining up across a row.
.ptt-shell .ptt-member-link{
	margin-top:auto; padding-top:16px;
	font-family:var(--ptt-display); font-weight:700; font-size:var(--ptt-fs-meta);
	color:var(--ptt-accent-ink);
} */


/* ==========================================================================
   12 · GALLERY — the BASE's arrangement, this layout's skin
   🔑 The widths, the per-count blocks and the lead/rest/tiles split are all the
   BASE's solved tokens keyed off data-pts-gal. A fork that writes its own grid
   gets none of that: measured on four sibling forks, one photo rendered beside
   367px of bare page, four wrapped 3+1, and a single photo came out a 1200×800
   slab against the base's 520px cap.
   ⚠️ So nothing here may restate a basis, a ratio, an aspect or align-items on
   a gallery cell — only the skin (gap, radius, fill, caption, hover).
   ========================================================================== */
.ptt-shell .pts-gallery{gap:14px}
/* ⚠️ Shell-prefixed AND naming the base's own container: `.pts-gallery figure`
   is (0,1,1), so a bare fork class (0,1,0) LOSES however late this sheet loads
   and the rule sits dead in the file. */
.ptt-shell .pts-gallery figure{
	border-radius:var(--ptt-r); overflow:hidden; border:0;
	background:var(--ptt-surface-2);
}
.ptt-shell .pts-gallery figcaption{
	font-family:var(--ptt-body-font); font-size:var(--ptt-fs-cap); font-weight:500;
	/* Fixed white on the scrim, never a theme token: the ground is an arbitrary
	   photograph. */
	color:#fff;
}


/* ==========================================================================
   13 · VIDEO — click-to-load
   ========================================================================== */
.ptt-shell .ptt-video{
	position:relative; aspect-ratio:16/9;
	max-height:520px; margin-inline:auto;
	border-radius:var(--ptt-r-lg); overflow:hidden;
	background:#0b0b0f;
}
/* ⚠️ An `aspect-ratio` box transfers its max-height into a max-width, so the
   auto inline margins are what centre it; a definite `width` would block that
   transfer and letterbox the player instead of scaling it. */
.ptt-shell .ptt-video .pts-video-poster{width:100%;height:100%;object-fit:cover;display:block}
.ptt-shell .ptt-video-play{
	position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
	width:64px; height:64px; border-radius:50%; border:0;
	display:flex; align-items:center; justify-content:center;
	/* 🔑 WHITE IS THE RESTING STATE, not a hover state — a play control's job is
	   to be unmissable the moment the section is on screen, and a hover a phone
	   never gets is not a state at all. Two contrast mechanisms, each covering
	   what the other cannot: on a light poster the DISC separates, on a dark one
	   the HALO does. */
	background:#fff; color:var(--ptt-accent-ink);
	box-shadow:0 0 0 1px rgba(255,255,255,.16),0 10px 40px 14px rgba(0,0,0,.42);
	transition:background-color var(--ptt-move),transform var(--ptt-move);
}
.ptt-shell .ptt-video-play:hover{background:var(--ptt-accent-soft);transform:translate(-50%,-50%) scale(1.07)}
/* The glyph box is half the disc; the path's stroke rounds its three points and
   scales with it, which is why the narrow block restates only this size. */
.ptt-shell .ptt-video-play svg{width:32px;height:32px;display:block}


/* ==========================================================================
   14 · REVIEWS — the summary panel beside the card list
   ========================================================================== */
.ptt-shell .ptt-reviews{display:flex;flex-wrap:wrap;gap:24px;align-items:flex-start}
/* 🔑 THE CHIPS + "Leave Review" TAKE A LINE OF THEIR OWN, above both columns —
   the base's own `.pts-review-actions{flex-basis:100%}` arrangement. Nested in
   the list's column instead it pushed the list down by its own height, so the
   summary panel and the list started at different heights; and with a single
   review there are no chips, so all that sat above the list was a lone
   right-aligned button reading as if it floated over it. */
.ptt-shell .ptt-reviews > .pts-review-actions{flex:1 1 100%;min-width:0}
.ptt-shell .ptt-review-summary{
	flex:0 1 var(--ptt-rev-sum); min-width:0;
	padding:24px 26px;
}
/* ⚠️ `flex:1 1 0`, and the zero basis is the point: flex breaks a line on an
   item's HYPOTHETICAL main size, so an `auto` basis here (max-content — the
   widest review row) would wrap the list onto its own line at widths where it
   could perfectly well have shrunk. */
.ptt-shell .ptt-reviews > .pts-review-list{flex:1 1 0;min-width:0}

/* 🔑 THE DIVIDER FOLLOWS THE STACK. The base's `.pts-rating-card` carries a RIGHT
   border, which is right in the base's own layout, where the average sits BESIDE
   the bars across the full page measure. In this 320px column they cannot: the
   card's 168px minimum plus the 24px gap plus the bars' 260px basis is 452
   against 266px of content, and flex breaks a line on that basis before it
   shrinks anything — so the bars wrapped underneath while the average kept a
   168px box, leaving its 1px divider and 26px of right padding hanging beside
   98px of empty panel (measured; reported as the summary "not aligned
   properly"). The answer is the base's own stacked variant: a rule UNDER the
   average, which is the same call Pawmi made for the same column.
   ⚠️ Shrinking the pair to fit side by side instead was measured and rejected —
   it leaves the whole distribution 105px wide, of which the `5★` label and the
   count take most, i.e. a bar that can no longer show a distribution.
   ⚠️ Anchored at (0,3,0) to beat the base's own `.pts-review-summary >
   .pts-rating-card` (0,2,0); a plainer selector loses however late this sheet
   loads. */
.ptt-shell .ptt-review-summary > .pts-rating-card{
	flex:1 1 100%;
	padding:0 0 18px;
	border-right:0;
	border-bottom:1px solid var(--ptt-line);
}


/* ==========================================================================
   15 · VISIT — map panel + contact card, then the hours cards
   ⚠️ THE PAIR IS EXACTLY TWO ROW CHILDREN. The hours card is child 3, and
   `:nth-child(odd)` is what gives it the full row — wrap the pair in a box, or
   add a third panel, and the hours card silently drops to half width.
   ========================================================================== */
.ptt-shell .ptt-visit{
	display:flex; flex-wrap:wrap; gap:var(--ptt-gap); align-items:flex-start;
}
.ptt-shell .ptt-map{
	flex:0 1 var(--ptt-map-basis); min-width:0;
	position:relative; aspect-ratio:16/10; cursor:pointer;
	border-radius:var(--ptt-r-lg); overflow:hidden;
	border:1px solid var(--ptt-line); background:var(--ptt-surface-2);
}
/* ⚠️ Nothing left to click in either state — the embed is already loaded, or the
   branch is a meeting link with nothing to embed. The base states exactly this
   (`.pts-loc-map.pts-map-live`), but at (0,2,0) it TIES with the pointer above
   and this sheet loads later, so it has to be restated at (0,3,0) here. */
.ptt-shell .ptt-map.pts-map-live,
.ptt-shell .ptt-map.pts-map-virtual{ cursor:default }
.ptt-shell .ptt-visit > .ptt-contact-card{
	flex:0 1 var(--ptt-loc-basis); min-width:0;
	padding:24px 26px;
}
/* 🔑 QUALIFIED WITH `.pts-loc-card`, and that is not belt-and-braces. § 20 has
   to strip the base's own `.pts-loc-card` styling (background, border, padding)
   because the base uses that class for a plain panel this layout draws itself —
   and that reset is (0,2,0), exactly the same weight as a plain
   `.ptt-shell .ptt-hours-card`, but it is written LATER in the file. A tie goes
   to the later rule, so unqualified this card would silently lose its own
   background, border and padding and render as bare page. */
.ptt-shell .ptt-hours-card.pts-loc-card{
	flex:0 1 var(--ptt-loc-basis); min-width:0;
	padding:24px 26px;
	background:var(--ptt-surface); border:1px solid var(--ptt-line);
	border-radius:var(--ptt-r-lg);
}
/* Alone on its line ⇒ the whole row. `:nth-child(odd)` IS the "alone" test (the
   card is child n+1, odd exactly when n is even), so it must stay conditional:
   unconditional would break the odd-count layouts, where the card is MEANT to
   sit beside the last location card. */
.ptt-shell .ptt-hours-card.pts-loc-card:nth-child(odd){flex-basis:100%}
.ptt-shell .ptt-hours-title{
	margin-bottom:18px;
	font-size:var(--ptt-fs-meta); font-weight:700; letter-spacing:.08em;
	text-transform:uppercase; color:var(--ptt-muted);
}
.ptt-shell .ptt-loc-title{
	font-family:var(--ptt-display); font-weight:700; font-size:var(--ptt-h3);
	letter-spacing:-.015em; color:var(--ptt-title);
}
.ptt-shell .ptt-addr{margin-top:8px;font-size:var(--ptt-fs-meta);color:var(--ptt-copy)}
.ptt-shell .ptt-dirs{
	display:inline-flex; align-items:center; gap:8px; margin-top:14px;
	font-size:var(--ptt-fs-meta); font-weight:600; color:var(--ptt-accent-ink);
}
.ptt-shell .ptt-dirs i{margin:0;width:auto;font-size:13px}


/* ==========================================================================
   16 · FAQ — native <details> rows
   ========================================================================== */
.ptt-shell .ptt-faq{border-top:1px solid var(--ptt-line)}
.ptt-shell .ptt-faq-row{border-bottom:1px solid var(--ptt-line)}
.ptt-shell .ptt-faq-row > summary{
	display:flex; align-items:center; justify-content:space-between; gap:20px;
	padding:22px 0; cursor:pointer; list-style:none;
	font-family:var(--ptt-display); font-weight:700; font-size:16px;
	letter-spacing:-.015em; color:var(--ptt-title);
}
/* The native marker differs per browser, so it is suppressed both ways and
   replaced by the CSS-drawn +/− below. */
.ptt-shell .ptt-faq-row > summary::-webkit-details-marker{display:none}
.ptt-shell .ptt-faq-row > summary::marker{content:''}
.ptt-shell .ptt-faq-mark{
	position:relative; flex:0 0 auto; width:20px; height:20px;
}
.ptt-shell .ptt-faq-mark::before,
.ptt-shell .ptt-faq-mark::after{
	content:''; position:absolute; left:50%; top:50%;
	background:var(--ptt-accent-ink);
	transition:transform var(--ptt-move),opacity var(--ptt-move);
}
.ptt-shell .ptt-faq-mark::before{width:14px;height:2px;transform:translate(-50%,-50%)}
.ptt-shell .ptt-faq-mark::after{width:2px;height:14px;transform:translate(-50%,-50%)}
.ptt-shell .ptt-faq-row[open] .ptt-faq-mark::after{opacity:0;transform:translate(-50%,-50%) rotate(90deg)}
/* ⚠️ An OPEN row hands its bottom air to the answer: at symmetric padding the
   gap inside a row is nearly the gap between rows, so proximity groups nothing
   and the answer reads as far from its own question as from the next one. */
.ptt-shell .ptt-faq-row[open] > summary{padding-bottom:8px}
.ptt-shell .ptt-faq-row p{
	padding-bottom:22px; font-size:var(--ptt-fs-body); line-height:1.6;
	color:var(--ptt-copy);
	/* The merchant's own line breaks survive on a page that escapes what it
	   renders. */
	white-space:pre-line;
}


/* ==========================================================================
   17 · CLOSING CTA — ink, answering the hero at the other end of the page
   ========================================================================== */
.ptt-shell .ptt-cta{background:var(--ptt-ink);padding:84px 0}
.ptt-shell .ptt-cta-in{text-align:center;max-width:640px;margin-inline:auto}
.ptt-shell .ptt-cta-title{
	font-size:40px; font-weight:700; line-height:1.08; letter-spacing:-.03em;
	text-transform:uppercase; color:var(--ptt-on-ink);
}
.ptt-shell .ptt-cta-in p{margin-top:16px;font-size:16px;color:var(--ptt-on-ink-body)}
.ptt-shell .ptt-cta-in .ptt-btn{margin-top:28px}


/* ==========================================================================
   18 · FOOTER — white
   🔑 A LIGHT FOOTER, so it reads as its own section under the ink CTA band.
   It was --ptt-ink-2 (#17191d) against the band's --ptt-ink (#0e1013): two
   near-blacks meeting edge to edge read as ONE section. On white every
   colour in it is the page's own light-ground set — --ptt-title / --ptt-copy
   type, --ptt-line hairlines, --ptt-accent-ink hovers — and the product credit
   takes the base's own light-footer rules, so none of it is re-stated here.
   ⚠️ --ptt-accent-lift is dark-ground only (2.59:1 on white) and must never
   come back into this section.
   No top hairline: the ink CTA band always renders directly above it, and the
   ink→white edge is the separation — a line there would only sit on that edge.
   ⚠️ No newsletter field, unlike the reference: there is no subscriber list
   behind it, so it would collect an address nothing ever sends to.
   ========================================================================== */
.ptt-shell .ptt-footer{
	background:var(--ptt-surface);
	padding:66px 0 0; color:var(--ptt-copy);
}
.ptt-shell .ptt-footer-top{display:flex;flex-wrap:wrap;gap:40px}
.ptt-shell .ptt-footer-brand{
	/* Takes the slack. ⚠️ A COLUMN, not a wrapping row: a paragraph with a
	   max-width has that cap as its flex basis, so `flex-basis:100%` stops
	   reserving a line and the description sits BESIDE the logo. */
	flex:1 1 300px; min-width:0;
	display:flex; flex-direction:column; align-items:flex-start; gap:14px;
}
/* The footer lockup sits tighter than the shared --pts-brand-gap (requested): scoped to
   .ptt-brand-footer (0,2,0), which ties .ptt-brand and wins on source order, so the
   header keeps the shared 6px. */
.ptt-shell .ptt-brand-footer{gap:5px}
/* ⚠️ The NAME must be re-inked too, not just the lockup: `.ptt-brand-name` states
   its own `color:#fff` for the dark header, which beats anything it would inherit
   — so on this white footer the wordmark rendered white-on-white beside a visible
   logo. (0,3,0) beats that (0,2,0). */
.ptt-shell .ptt-brand-footer,
.ptt-shell .ptt-brand-footer .ptt-brand-name{color:var(--ptt-title)}
.ptt-shell .ptt-footer-copy{max-width:340px;font-size:var(--ptt-fs-meta)}
/* The footer's social plates are the plain `.ptt-social` — the same light-ground
   plate the identity row and the contact block wear, so no override here.
   🔑 The product credit ("Made with ♥ by Picktime" + its two product lines) is
   the base's block, and on this LIGHT footer the base's own rules are already
   right — `--pts-text`/`--pts-muted` type, `--pts-accent-ink` hover and the
   light `--pts-heart` (#d8314b) — so nothing is re-stated. While the footer was
   ink, all four had to be (the first line measured 1.13:1 there); don't bring
   those overrides back without bringing the ink back. */
.ptt-shell .ptt-footer-col{flex:0 1 180px;min-width:0}
.ptt-shell .ptt-footer-col h4{
	margin-bottom:16px;
	font-size:var(--ptt-fs-cap); font-weight:700; letter-spacing:.1em;
	text-transform:uppercase; color:var(--ptt-title);
}
.ptt-shell .ptt-footer-col ul{margin:0;padding:0;list-style:none}
.ptt-shell .ptt-footer-col li + li{margin-top:10px}
.ptt-shell .ptt-footer-col a{
	font-size:var(--ptt-fs-meta); color:var(--ptt-copy);
	transition:color var(--ptt-move);
}
.ptt-shell .ptt-footer-col a:hover{color:var(--ptt-accent-ink)}
/* 🔑 THE WHOLE BAR IS THE FLOATING PILL'S BAND — measured from the HAIRLINE, not
   from the copyright line, which is the half that is easy to get wrong and was
   (reported as "more padding bottom below the copyright" on a large screen). The
   pill is 291px wide and rests bottom-RIGHT, so it sits BESIDE the copyright, not
   under it: the base states the same band as
        hairline (1) + padding-top (20) + copyright line (18) + its own padding
        = --pts-cta-h + 2 * --pts-cta-gap
   i.e. 76px from the hairline to the page's end, which leaves the pill 11px of air
   either side and only ~37px of padding below the line. Reserving those 76px BELOW
   the line instead measured a 117.6px bar — 42px of dead ink slab under a page
   that had already ended.
   ⚠️ DERIVED, never chosen: the same three terms as the base's, in this layout's
   own numbers (1px hairline + 22px padding-top + one line). A hard-coded value
   here has been wrong three times in the base's own history.
   ⚠️ The line is `em` of this bar's own 12px type, which is only correct because
   this shell's `line-height` is UNITLESS (1.55, measured): a length there would
   inherit as a fixed px value and the em would reserve the wrong height. */
.ptt-shell{--ptt-footer-band:calc(var(--pts-cta-h) + var(--pts-cta-gap) * 2)}
.ptt-shell .ptt-footer-bar{
	display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:14px;
	margin-top:52px; padding:22px 0 calc(var(--ptt-footer-band) - 23px - 1.55em);
	border-top:1px solid var(--ptt-line);
	font-size:var(--ptt-fs-cap);
	/* 🔑 AND THE EMPTY BAR OCCUPIES THE SAME BAND. A white-labeled page renders no
	   copyright (naming Picktime there is exactly what that add-on pays to remove),
	   so without a floor the bar loses a line's height and its hairline — this
	   footer's closing rule — drops onto the pill. Border-box, so the band IS the
	   floor and the two can never state different strips. */
	min-height:var(--ptt-footer-band);
}
/* 🔑 THE PILL IS THE PRODUCT'S OWN CHROME AND KEEPS THE BASE'S BOX — the same
   doctrine as the Picktime credit plate: a layout may re-point its colours, never
   its geometry, so the same object does not read as two different objects on two
   Picktime booking pages. Its height is `--pts-cta-h` (54px) = 8 + 36 + 8 + 2,
   where 36 is BOTH the base's `pts-btn-small` and the copy block's two lines at
   the base's 1.5 leading (13px + 11px). This shell's own leading is 1.55, which
   made the copy block 37.2px and the pill 55.2 — 1.2px over the strip reserved
   for it above. Nothing else here is restated: the base's palette is already
   re-pointed onto this layout's tokens, so it wears Torqa's colours for free. */
.ptt-shell .pts-scroll-cta{line-height:1.5}
.ptt-shell .ptt-legal{display:flex;flex-wrap:wrap;gap:18px}
.ptt-shell .ptt-footer .ptt-legal a{font-size:var(--ptt-fs-cap);color:var(--ptt-copy)}
.ptt-shell .ptt-footer .ptt-legal a:hover{color:var(--ptt-accent-ink)}
/* Renders empty on desktop — it is placeLang()'s landing site below 921px. */
.ptt-shell .pts-footer-brand:empty{display:none}
/* 🔑 placeLang() MOVES the header's language chip into this footer below 921px —
   one node, not a copy — and the header rules above ink it for the transparent
   header over the hero (white text, white ring). On this white footer those are
   1:1, i.e. an invisible chip on the page's only door to its other locales. The
   footer scope IS the condition (the chip takes header colours while it is in
   the header), so this needs no media query; the hover is restated too, since
   it carries its own colour. (0,3,0)/(0,4,0) beat the header's (0,2,0)/(0,3,0). */
.ptt-shell .ptt-footer .pts-lang{border-color:var(--ptt-line);color:var(--ptt-copy)}
.ptt-shell .ptt-footer .pts-lang:hover{
	border-color:var(--ptt-accent-edge); color:var(--ptt-accent-ink); background:var(--ptt-accent-soft);
}


/* ==========================================================================
   19 · RESPONSIVE
   ⚠️ Every override matches the anchoring of the rule it overrides (§ 0 rule 3)
   and the blocks stay in DESCENDING order, since a media query adds no
   specificity and equal-specificity rules are decided by source order alone.
   ========================================================================== */

/* The header nav has its OWN collapse point, well above the layout's. That row
   is brand + nav + actions and nothing in it can give way: a nav label does not
   compress and the actions are the page's controls. The NAV is what yields —
   every entry it holds is also in the footer's columns, while the brand, the
   account chip and Book now must stay whole. */
@media (max-width:1140px){
	.ptt-shell .ptt-nav{display:none}
	.ptt-shell{
		--ptt-card-basis:calc((100% - (var(--ptt-gap) * 2)) / 3);
		--ptt-h1:54px;
	}
}

@media (max-width:920px){
	.ptt-shell{
		--ptt-card-basis:calc((100% - var(--ptt-gap)) / 2);
		--ptt-hours-basis:calc((100% - 24px) / 3);
		--ptt-band-pad:64px;
		--ptt-h1:42px;
		--ptt-h2:27px;
	}
	.ptt-shell .ptt-hero{min-height:560px;padding:128px 0 110px}
	/* The scrim turns vertical once the copy fills the width: a horizontal wash
	   leaves the right-hand end of every line on the bare photograph. */
	.ptt-shell .ptt-hero-scrim{
		background:linear-gradient(180deg,rgba(9,10,12,.72) 0%,rgba(9,10,12,.84) 100%);
	}
	/* ⚠️ Both panels take the whole line; `0 1 330px` would otherwise leave the
	   start panel beside a squeezed identity card. */
	.ptt-shell .ptt-id-card,
	.ptt-shell .ptt-start-card{flex:1 1 100%}
	.ptt-shell .ptt-identity{margin-top:-64px}
	/* ⚠️ Anchored the same way as the base rules they override — a bare
	   `.ptt-review-summary{flex-basis:100%}` would lose on specificity. */
	.ptt-shell .ptt-review-summary{flex-basis:100%}
	.ptt-shell .ptt-reviews > .pts-review-list{flex:1 1 100%}
	.ptt-shell .ptt-map,
	.ptt-shell .ptt-visit > .ptt-contact-card{flex-basis:100%}
	/* ⚠️ Carries `.pts-loc-card` for the same reason the desktop rule does — and
	   here it also has to out-rank the `:nth-child(odd)` rule above, which is
	   (0,4,0). At (0,3,0) it beats the plain (0,3,0) desktop rule, and the odd
	   case already resolves to 100% anyway. */
	.ptt-shell .ptt-hours-card.pts-loc-card{flex-basis:100%}
	.ptt-shell .ptt-details{flex:1 1 100%}
	.ptt-shell .ptt-cta-title{font-size:32px}
}

@media (max-width:720px){
	.ptt-shell{
		--ptt-hours-basis:calc((100% - 12px) / 2);
		--ptt-gutter:18px;
		--ptt-band-pad:54px;
		--ptt-h1:34px;
		--ptt-h2:24px;
	}
	.ptt-shell .ptt-hero{min-height:500px;padding:112px 0 92px}
	.ptt-shell .ptt-id-mark{width:60px;height:60px;font-size:20px}
	.ptt-shell .ptt-id-name{font-size:19px}
	/* The head keeps its action on the right at every width — only the gap
	   tightens. Stacking it parks the action at the left edge under the heading,
	   where it reads as a stray line of copy. */
	.ptt-shell .ptt-head{gap:14px;margin-bottom:26px}
	.ptt-shell .ptt-footer-top{gap:30px}
	.ptt-shell .ptt-footer-col{flex:0 1 140px}
	/* The arrows are shown at every width (§ 9); they are trimmed here because
	   the head row now carries them beside a heading that has also stepped down,
	   and a 36px pair beside a 13px label reads as the loudest thing in the row.
	   ⚠️ No `left`/`right` counterparts any more — they are static in the row,
	   so there is no offset to re-tune. Anchored `.ptt-shell` like the rules they
	   override: a media query adds no specificity. */
	.ptt-shell .ptt-rail-arrow{width:32px;height:32px}
	.ptt-shell .ptt-rail-arrow i{font-size:12px}
	.ptt-shell .ptt-cat-head{gap:12px;min-height:32px}
	/* 🔑 THE PHONE'S BOTTOM CONTROL COVERS RATHER THAN SITS BESIDE, so this is a
	   CLEARANCE, not a band — the one place the desktop derivation above does not
	   carry over. `.pts-mobile-cta` is FULL WIDTH (10px off the bottom, measured
	   57.2px tall), so the copyright cannot share its line the way it shares the
	   pill's: the padding has to clear the bar BELOW the content, exactly as the
	   base states it (`.pts-footer{padding-bottom:86px}` — a rule naming a class
	   this layout does not render, which is why it is restated here at all).
	   ⚠️ The band's `min-height` is released with it: at 86px it would put the
	   copyright 41.6px into a strip whose bottom 67.2px the bar paints over.
	   ⚠️ The cookie notice needs no counterpart: the base lifts it to `bottom:79px`
	   in this same block with a bare `.pts-cookie` rule, and this sheet's own
	   `.pts-cookie` states colours only — the moment it states a `bottom`, that
	   (0,2,0) rule wins and the notice sits on the bar (Kinetra's reported bug). */
	.ptt-shell .ptt-footer-bar{padding-bottom:86px;min-height:auto}
}

@media (max-width:560px){
	.ptt-shell{
		--ptt-card-basis:100%;
		--ptt-hours-basis:100%;
		--ptt-h1:29px;
	}
	.ptt-shell .ptt-header{padding:16px 0}
	.ptt-shell .ptt-brand{max-width:150px}
	.ptt-shell .ptt-actions{gap:8px}
	.ptt-shell .ptt-video-play{width:52px;height:52px}
	.ptt-shell .ptt-video-play svg{width:26px;height:26px}
	.ptt-shell .ptt-footer-col{flex:1 1 100%}
	.ptt-shell .ptt-cta-title{font-size:26px}
}


/* ==========================================================================
   20 · BASE OVERRIDES
   The base sheet's ELEMENT-level `.pts-*` rules reach every hook this template
   reuses. These are the ones that would otherwise show through: the header
   controls, the hero cover, the in-page nav, the catalogue's "load more" and
   Read More fold, the reviews subsystem, the shared dialogs, the hours card and
   the cookie notice.
   ⚠️ Renaming a hook in the template means deleting its override here.
   ========================================================================== */

/* ---- the base's cover box ---------------------------------------------
   🔑 `.pts-cover` is the hook bindCoverRatio() queries (via `.pts-cover img`),
   so the class has to stay — but here the box is an ABSOLUTE FILL behind the
   headline, not a banner in the flow, so every geometric thing the base gives
   it has to go: the fixed 4:1 measure, the max-width cap, the centring margins
   and its `::after` scrim (this layout paints its own, tuned for text sitting
   ON the picture). */
.ptt-shell .ptt-hero-media.pts-cover{
	aspect-ratio:auto; max-width:none; max-height:none; border-radius:0;
}
.ptt-shell .ptt-hero-media.pts-cover::after{content:none}

/* ---- header controls -------------------------------------------------- */
.ptt-shell .pts-lang,.ptt-shell .pts-login{
	display:inline-flex; align-items:center; gap:6px;
	height:40px; padding:0 14px; border-radius:var(--ptt-r-pill);
	border:1px solid rgba(255,255,255,.28); background:none;
	font-family:var(--ptt-body-font); font-size:var(--ptt-fs-meta); font-weight:500;
	line-height:1.2; color:rgba(255,255,255,.88); white-space:nowrap;
	transition:border-color var(--ptt-move),color var(--ptt-move),background-color var(--ptt-move);
}
.ptt-shell .pts-lang:hover,.ptt-shell .pts-login:hover{
	border-color:#fff; color:#fff; background:rgba(255,255,255,.08);
}
.ptt-shell .pts-login{font-family:var(--ptt-display);font-weight:700}
.ptt-shell .pts-lang-label{
	max-width:150px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.ptt-shell .pts-caret{font-size:10px;margin:0;width:auto}
.ptt-shell .pts-user{position:relative}
.ptt-shell .pts-user-btn{
	display:inline-flex; align-items:center; gap:8px;
	height:40px; padding:0 14px 0 6px; border-radius:var(--ptt-r-pill);
	border:1px solid rgba(255,255,255,.28); background:none; color:#fff;
	transition:border-color var(--ptt-move),color var(--ptt-move),background-color var(--ptt-move);
}
/* The account chip is the third of the three pills in this row and shipped with NO hover at all,
   so pointing at it did nothing while its two siblings above lifted — and the signed-in and
   signed-out headers occupy the SAME slot, so a visitor got feedback in one state and none in the
   other. Same declarations as `.pts-lang:hover`/`.pts-login:hover`, deliberately: this header is
   transparent over the hero photograph, so all three are the same ghost pill.
   ⚠️ It also has to be stated rather than left to the base: `.pts-user-btn:hover` (0,2,0) sets
   `background:var(--pts-surface-2)`, and it only lost here because the rest rule above ties with it
   and wins on source order — the same near-miss that painted a white pill on Kinetra's header. */
.ptt-shell .pts-user-btn:hover{border-color:#fff; color:#fff; background:rgba(255,255,255,.08)}
.ptt-shell .pts-user-btn .pts-avatar{
	width:28px; height:28px; border-radius:50%;
	display:flex; align-items:center; justify-content:center;
	background:var(--ptt-accent); color:var(--pts-accent-on);
	font-family:var(--ptt-display); font-weight:700; font-size:12px; line-height:1;
}
.ptt-shell .pts-user-name{
	max-width:130px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
	font-size:var(--ptt-fs-meta);
}
.ptt-shell .pts-user-menu{
	display:none; position:absolute; right:0; top:calc(100% + 10px); z-index:80;
	min-width:230px; padding:8px;
	background:var(--ptt-surface); border:1px solid var(--ptt-line);
	border-radius:var(--ptt-r); box-shadow:0 18px 44px rgba(14,16,19,.14);
}
/* ⚠️ `.pts-open` is TWO things on a booking homepage — the user menu's open
   state AND the business's open/closed status chip — so this must stay scoped
   to `.pts-user`, or opening the dropdown repaints the status chip. */
.ptt-shell .pts-user.pts-open .pts-user-menu{display:block}
.ptt-shell .pts-user-menu-head{padding:10px 12px 12px;border-bottom:1px solid var(--ptt-line)}
.ptt-shell .pts-user-menu-head strong{
	display:block; font-family:var(--ptt-display); font-weight:700;
	font-size:var(--ptt-fs-body); color:var(--ptt-title);
}
.ptt-shell .pts-user-menu-head span{display:block;margin-top:2px;font-size:var(--ptt-fs-cap);color:var(--ptt-muted)}
.ptt-shell .pts-user-menu a{
	display:block; padding:10px 12px; border-radius:var(--ptt-r-sm);
	font-size:var(--ptt-fs-meta); color:var(--ptt-title);
	transition:background-color var(--ptt-move);
}
.ptt-shell .pts-user-menu a:hover{background:var(--ptt-surface-2)}

/* ---- the Picktime credit badge ----------------------------------------
   🔑 STYLED ENTIRELY BY THE BASE — this layout states NOTHING about it, and that
   is the point: the product is credited ONE way across every Picktime booking
   page, so the plate that says "Powered by Picktime" here is the same object,
   at the same size and spacing, as the one on the seven designs. The template
   already emits the base's own markup and classes (.pts-brand-badge + -top +
   -name), so handing the sheet back to bookinghome-designs.css is the whole of
   it — and a change to the shared plate then reaches this layout for free
   instead of having to be mirrored here.
   ⚠️ It reads --pts-accent / --pts-accent-on / --pts-badge-sub, which resolve
   per the merchant's BOOKING PAGE THEME (§ 0: this layout wears the account's
   swatch rather than a palette of its own), so the plate is theme-correct here
   without a single local declaration.
   ⚠️ DO NOT restate its box. It carried `gap:1px; height:40px; padding:0 13px`,
   an 8px radius, a 1px border and the display face at weight 700 — every one of
   those a deviation from the base's `gap:3px; padding:7px 12px`, 6px radius, no
   border and 15px/800, i.e. the credit read as a different, tighter object from
   the one on the other seven layouts. The base also trims it at <=425px
   (`padding:6px 10px`, name 14px); the copy of that trim which used to live in
   this file went with the rest. */

/* ---- the base's in-page pill row: kept in the DOM, hidden ---------------
   ⚠️ HIDDEN, not deleted. bindNavScroll still measures .pts-nav-inner and the
   two .pts-nav-arrow buttons, and honourSectionHash plus the scroll spy address
   the section ids. This layout's nav is the row in the floating header. */
.ptt-shell .pts-nav{display:none}

/* ---- the TEAM's Load more + the Read More fold --------------------------
   ⚠️ The team grid is the ONLY .pts-cat-more left on the page — the catalogue
   opts out of the collapse (its rail's arrows are how you see the rest), which
   is why this centres itself unconditionally instead of scoping the centring to
   `.ptt-team + .pts-cat-more`. Restore that scope if a second one ever lands. */
.ptt-shell .pts-cat-more{
	display:flex; align-items:center; justify-content:center;
	margin:22px auto 0; height:42px; padding:0 22px;
	border:1px solid var(--ptt-line); border-radius:var(--ptt-r-pill);
	background:var(--ptt-surface);
	font-family:var(--ptt-display); font-weight:700; font-size:13.5px;
	color:var(--ptt-title);
	transition:border-color var(--ptt-move),color var(--ptt-move);
}
.ptt-shell .pts-cat-more:hover{border-color:var(--ptt-accent-edge);color:var(--ptt-accent-ink)}
/* ⚠️ `margin:0` — the card body's own `gap` owns the rhythm, and the base's
   `.pts-tile-desc` carries a top margin that would double the step. */
.ptt-shell .pts-tile-desc{
	margin:0; font-size:var(--ptt-fs-meta); line-height:1.55; color:var(--ptt-copy);
}
/* ⚠️ The fold stays at ONE line. Give a description more room with a taller
   card, never with another line. */
.ptt-shell .ptt-item{--pts-desc-lines:1}
/* 🔑 THE WRAPPER IS NEUTRALISED TO A BLOCK AT REST — the "Qitchen route", and
   the reason it is the right one here is that this card's body is already a
   flex COLUMN with its own `gap`, so the description and its Read More read as
   two stacked lines like everything else in the card.
   ⚠️ The alternative is to leave the base's `display:flex; align-items:baseline`
   and mirror `pts-expanded` onto this layout's card in the view — the base's own
   `.pts-tile.pts-expanded` rules cannot reach a card that is not `.pts-tile`.
   Both are honest; what is NOT is leaving the flex line with no mirror, which
   parks "Read Less" beside line ONE with the rest of the description running on
   underneath it. Taking this route is why the view carries no `bindTextFolds`
   override. Shell-prefixed (0,2,0) to beat the base's bare `.pts-desc-row`
   (0,1,0). Guard: BookingHomeForkTextDoctrineTest. */
.ptt-shell .pts-desc-row{display:block}
.ptt-shell .pts-tile-more{
	position:relative; z-index:2; pointer-events:auto;
	font-family:var(--ptt-display); font-weight:700; font-size:var(--ptt-fs-cap);
	color:var(--ptt-accent-ink); background:none; border:0; padding:0;
}

/* ---- the hours card's rows, drawn as the reference's day CARDS ----------
   🔑 renderHours() REPLACES the whole of #ptsHoursList with `.pts-hours-row`
   rows once the timezone-converted schedule lands, so the card appearance MUST
   be CSS on the base's class — a fork that emitted `.ptt-hours-row` would style
   rules that match nothing at runtime and the section would silently revert to
   the base's list look.
   ⚠️ AND EVERY DECLARATION IS STATED FOR TODAY TOO. The base's own
   `.pts-hours-row.pts-today strong` (0,2,1) and
   `…span:not(.pts-hours-badge)` (0,3,1) TIE with a plainly-written fork rule,
   and a tie goes to the later sheet — which means the fork wins only the
   declarations it happens to make and the base silently supplies the rest. That
   is how a sibling fork ended up with an 800-weight value column (reported as
   "the text is zoomed") and a purple day name its palette does not contain. */
/* 🔑 THE SHORT LINE IS CENTRED, and that is not a stylistic preference — a WEEK
   IS SEVEN, A PRIME, so no column count above 1 ever divides it and every ladder
   step ends on a partial line: 4+3 at 4-up, 3+3+1 at 3-up, 2+2+2+1 at 2-up. Left
   flush, that trailing card sat against the left edge with two empty cells beside
   it and read as a FAILED RENDER rather than as the end of the week (reported
   exactly that way). Centring it is the base's own rule for a prime count, and it
   is a no-op on every full line — those divide the row exactly, so nothing moves. */
/* 🔑 QUALIFIED WITH THE ID, AND THAT IS THE WHOLE FIX FOR THE COLUMN COUNT. The
   base gives its own full-width hours card `#pts-hours:nth-child(odd)
   .pts-hours{columns:2;column-gap:34px}` — an ID selector, (1,2,0), which beats
   ANY number of classes, so the plain `.ptt-shell .ptt-hours-card .pts-hours`
   (0,3,0) lost it. `columns` is inert on a flex container and looked harmless;
   `column-gap` is NOT — it IS the flex gap. So the row ran on a 34px gap while
   --ptt-hours-basis was solved for 12px, and four cards that should have divided
   the line exactly came to 1162px in a 1098px container: the week rendered
   3 + 3 + 1 instead of 4 + 3, at every width, with a lone card centred on a line
   of its own. ⚠️ A basis is not the thing to adjust when a solved count does not
   hold — measure the gap the row actually got first. */
.ptt-shell .ptt-hours-card#pts-hours .pts-hours,
.ptt-shell .ptt-hours-card .pts-hours{
	display:flex; flex-wrap:wrap; justify-content:center; gap:12px;
}
/* 🔑 TWO LINES, NEVER THREE: day + status on the first, the hours on the second.
   The three parts were stacked, which left the pill alone on a line of its own
   with the base's `margin-left:auto` (0,2,0) still pushing it to the far edge —
   a chip floating in the bottom-right corner of the card, a clear head taller
   than the copy it belonged to and 20-30px of dead ground between them. The pill
   reads as the DAY's status, so it belongs on the day's line; the hours then get
   the card's full width, which is what lets a split shift ("9:00 AM to 1:00 PM,
   2:00 PM to 6:00 PM") wrap without pushing anything sideways.
   ⚠️ `order` is what puts the badge there — the markup is the base's
   (day, value, badge) and must stay that way, so the VISUAL order is CSS's job.
   Reading order is unchanged for AT, which is the order that has to be logical. */
.ptt-shell .pts-hours-row,
.ptt-shell .pts-hours-row.pts-today{
	flex:0 1 var(--ptt-hours-basis); min-width:0;
	display:flex; flex-wrap:wrap; align-items:center; gap:7px 10px;
	/* ⚠️ THE TWO LINES STACK FROM THE TOP, they are not centred in the card. A
	   card stretches to the tallest in its line, and a business with a split
	   shift ("9:00 AM to 1:00 PM, 2:00 PM to 6:00 PM") has one card whose hours
	   wrap — centred, that card's extra height pushed its own day name 5px ABOVE
	   its neighbours' and the row of bold day names, which is what the eye tracks
	   across a week, stopped being a line. The slack falls at the foot of the
	   card instead, where nothing is aligned against it. */
	align-content:flex-start;
	padding:14px 16px; margin:0; border:1px solid var(--ptt-line);
	border-radius:var(--ptt-r); background:var(--ptt-surface);
}
.ptt-shell .pts-hours-row strong,
.ptt-shell .pts-hours-row.pts-today strong{
	order:1; flex:1 1 auto; min-width:0;
	font-family:var(--ptt-display); font-weight:700; font-size:var(--ptt-fs-meta);
	letter-spacing:-.005em; color:var(--ptt-title);
}
/* ⚠️ `flex:0 0 100%` — a whole line of its own, so every card in a row breaks at
   the same place whatever its day name and value happen to measure. `auto` here
   let a short value ("Closed") ride up beside the day on some cards and not on
   others, so the column of hours came apart down the list.
   `tabular-nums` is what lines the times up card to card: proportional digits
   make "11:55" narrower than "12:00" and the ranges stop agreeing on a left
   edge — the one typographic detail that decides whether a week reads as a
   table or as seven unrelated boxes. */
.ptt-shell .pts-hours-row > span:not(.pts-hours-badge),
.ptt-shell .pts-hours-row.pts-today > span:not(.pts-hours-badge){
	order:3; flex:0 0 100%; min-width:0;
	font-family:var(--ptt-body-font); font-weight:400; font-size:var(--ptt-fs-meta);
	font-variant-numeric:tabular-nums; color:var(--ptt-copy);
}
/* Today's card is the accent fill, which is the reference's own treatment — with
   a shadow in its own colour, so the day a visitor is actually reading lifts off
   the row instead of merely being a differently-coloured box in it. */
.ptt-shell .pts-hours-row.pts-today{
	background:var(--ptt-accent); border-color:var(--ptt-accent);
	box-shadow:0 10px 24px rgba(var(--ptt-accent-rgb),.22);
}
.ptt-shell .pts-hours-row.pts-today strong{color:var(--pts-accent-on)}
.ptt-shell .pts-hours-row.pts-today > span:not(.pts-hours-badge){color:var(--pts-badge-sub)}
/* ⚠️ The badge is qualified with BOTH its classes so the selector reaches
   (0,3,0)/(0,4,0) — the base tints ITS today pills a step stronger
   (`.pts-hours-row.pts-today .pts-badge-open`, 0-3-0), which out-ranks a bare
   two-class fork rule and gives today's pill a different colour from every
   other row's. */
.ptt-shell .pts-hours-row .pts-hours-badge{
	order:2; flex:0 0 auto; margin-left:auto;
	padding:3px 10px; border-radius:var(--ptt-r-pill);
	font-family:var(--ptt-body-font); font-size:var(--ptt-fs-micro); font-weight:600;
	white-space:nowrap;
}
.ptt-shell .pts-hours-row .pts-hours-badge.pts-badge-open{background:var(--ptt-success-bg);color:var(--ptt-success)}
.ptt-shell .pts-hours-row .pts-hours-badge.pts-badge-closed{background:var(--ptt-danger-bg);color:var(--ptt-danger)}
/* On the filled card both pills go to a translucent white plate — the light
   tints above are invisible on the accent fill, whatever the theme. The label
   is --pts-accent-on, so on the yellow swatch the plate carries dark ink. */
.ptt-shell .pts-hours-row.pts-today .pts-hours-badge.pts-badge-open,
.ptt-shell .pts-hours-row.pts-today .pts-hours-badge.pts-badge-closed{
	background:rgba(255,255,255,.2); color:var(--pts-accent-on);
}

/* ---- location: the map panel + the contact / branch cards -------------- */
.ptt-shell .ptt-map .pts-map-note strong{
	font-family:var(--ptt-display); font-weight:700; color:var(--ptt-title);
}
.ptt-shell .pts-loc-card{background:none;border:0;border-radius:0;padding:0;box-shadow:none}
.ptt-shell .ptt-contact-card.pts-loc-card{
	background:var(--ptt-surface); border:1px solid var(--ptt-line);
	border-radius:var(--ptt-r-lg); padding:24px 26px;
}
.ptt-shell .pts-contact-row{
	display:flex; align-items:flex-start; gap:12px; min-width:0;
	font-size:var(--ptt-fs-meta); color:var(--ptt-copy);
}
.ptt-shell .pts-contact-row + .pts-contact-row{margin-top:12px}
.ptt-shell .pts-contact-icon{
	flex:0 0 auto; width:auto; height:auto; background:none;
	font-size:14px; line-height:1.5; color:var(--ptt-accent-ink);
}
.ptt-shell .pts-contact-row a{color:var(--ptt-copy);font-size:inherit}
.ptt-shell .pts-contact-row a:hover{color:var(--ptt-accent-ink)}
.ptt-shell .pts-loc-follow h4,.ptt-shell .pts-branch-contact h4,.ptt-shell .ptt-branch-head{
	margin:0 0 12px;
	font-family:var(--ptt-display); font-weight:700; font-size:var(--ptt-fs-meta);
	letter-spacing:.06em; text-transform:uppercase; color:var(--ptt-muted);
}
.ptt-shell .pts-loc-follow{margin-top:20px;padding-top:18px;border-top:1px solid var(--ptt-line)}
.ptt-shell .pts-branch-contact{margin-top:18px;padding-top:18px;border-top:1px solid var(--ptt-line)}
.ptt-shell .pts-branch-on{background:var(--ptt-accent-soft);border-radius:var(--ptt-r)}
.ptt-shell .pts-branch-copy strong{
	font-family:var(--ptt-display); font-weight:700; font-size:var(--ptt-fs-body); color:var(--ptt-title);
}
.ptt-shell .pts-branch-link{color:var(--ptt-accent-ink);font-size:var(--ptt-fs-cap)}

/* ---- reviews ----------------------------------------------------------- */
.ptt-shell .ptt-review-summary.pts-review-summary{
	background:var(--ptt-surface); border:1px solid var(--ptt-line);
	border-radius:var(--ptt-r-lg);
}
.ptt-shell .pts-rating-card #ptsReviewAvg{
	font-family:var(--ptt-display); font-weight:700; font-size:52px; line-height:1;
	letter-spacing:-.04em; color:var(--ptt-title);
	/* The line's height is reserved so nothing jumps when renderReviews() fills
	   it a moment after first paint. */
	min-height:52px; display:block;
}
.ptt-shell .pts-star{color:#f2a93b}
.ptt-shell .pts-star.sempty{color:var(--ptt-line)}
.ptt-shell .pts-bar-fill{background:var(--ptt-accent)}
.ptt-shell .pts-bar-track{background:var(--ptt-surface-2)}
.ptt-shell .pts-chip{
	border-radius:var(--ptt-r-pill); border:1px solid var(--ptt-line);
	background:var(--ptt-surface); color:var(--ptt-copy);
	font-family:var(--ptt-body-font); font-size:var(--ptt-fs-cap);
}
/* Same "match around the selected one" rule as the catalogue chips above. */
.ptt-shell .pts-chip:not(.pts-chip-on):hover{
	border-color:var(--ptt-accent-edge); color:var(--ptt-accent-ink);
}
.ptt-shell .pts-chip.pts-chip-on{background:var(--ptt-accent);border-color:var(--ptt-accent);color:var(--pts-accent-on)}
/* 🔑 A REVIEW IS A ROW, NOT A CARD — and this block deliberately states NOTHING
   about its background, border or radius.
   The base makes `.pts-review-list` the single bordered panel (background,
   border, radius and 22px of side padding) and each `.pts-review` a row inside
   it: `padding:19px 0` with a hairline under it and none under the last. Giving
   the row a card of its own therefore produces TWO nested borders — and because
   the row has no horizontal padding of its own, its content then sits flush on
   that inner border: the comment text touches the left edge and the trailing
   score is clipped by the corner radius. Both were reported from the live page.
   The panel already wears this layout's colours through the re-pointed
   --pts-surface / --pts-line / --pts-radius, so there is nothing to restate. */
.ptt-shell .pts-reviewer{
	font-family:var(--ptt-display); font-weight:700; color:var(--ptt-title);
}
.ptt-shell .pts-review-date{color:var(--ptt-muted);font-size:var(--ptt-fs-cap)}
/* The base writes the comment as both `.pts-review-text` and a bare `<p>` inside
   the row depending on the surface, so both are named — one of them alone
   leaves the other on the base's grey. */
.ptt-shell .pts-review-text,
.ptt-shell .pts-review p{color:var(--ptt-copy);font-size:var(--ptt-fs-meta)}
.ptt-shell .pts-review-reply{background:var(--ptt-surface-2);border-radius:var(--ptt-r-sm)}
.ptt-shell .pts-review-reply p{color:var(--ptt-copy)}
.ptt-shell .pts-review-more,.ptt-shell .pts-review-edit,.ptt-shell .pts-review-all{
	font-family:var(--ptt-display); font-weight:700; color:var(--ptt-accent-ink);
}

/* ---- shared dialogs ----------------------------------------------------
   Repainted for this palette; their behaviour is the base's. */
.ptt-shell .pts-modal-panel{border-radius:var(--ptt-r-lg);background:var(--ptt-surface)}
.ptt-shell .pts-modal-head h2{
	font-family:var(--ptt-display); font-weight:700; letter-spacing:-.02em; color:var(--ptt-title);
}
.ptt-shell .pts-modal-close{color:var(--ptt-muted)}
.ptt-shell .pts-modal-close:hover{color:var(--ptt-title)}
.ptt-shell .pts-btn{
	font-family:var(--ptt-display); font-weight:700; border-radius:var(--ptt-r-pill);
}
.ptt-shell .pts-btn-primary{background:var(--ptt-accent);border-color:var(--ptt-accent);color:var(--pts-accent-on)}
.ptt-shell .pts-btn-primary:hover{background:var(--ptt-accent-deep);color:var(--pts-accent-on)}
.ptt-shell .pts-btn-secondary{
	background:var(--ptt-surface); border:1px solid var(--ptt-line); color:var(--ptt-title);
}
.ptt-shell .pts-form-label{
	font-family:var(--ptt-display); font-weight:700; font-size:var(--ptt-fs-meta); color:var(--ptt-title);
}
.ptt-shell #ptsReviewComment{
	border-radius:var(--ptt-r); border:1px solid var(--ptt-line); background:var(--ptt-surface-2);
	font-family:var(--ptt-body-font); font-size:var(--ptt-fs-body); color:var(--ptt-title);
}
.ptt-shell #ptsReviewComment:focus{border-color:var(--ptt-accent);outline:none}
.ptt-shell .pts-char-count{color:var(--ptt-muted);font-size:var(--ptt-fs-cap)}
.ptt-shell .pts-form-error{color:var(--ptt-danger);font-size:var(--ptt-fs-cap)}
.ptt-shell .pts-lb-img{border-radius:var(--ptt-r)}

/* ---- cookie notice ----------------------------------------------------- */
.ptt-shell .pts-cookie{
	background:var(--ptt-surface); border:1px solid var(--ptt-line);
	border-radius:var(--ptt-r); box-shadow:0 18px 44px rgba(14,16,19,.14);
}
.ptt-shell .pts-cookie p{font-size:var(--ptt-fs-cap);color:var(--ptt-copy)}
.ptt-shell .pts-cookie a{color:var(--ptt-accent-ink);font-size:inherit}
.ptt-shell .pts-cookie-close{color:var(--ptt-muted)}


/* ==========================================================================
   21 · PHONE HEADER — the base sheet's own ≤768 / ≤425 steps, restated at THIS
   layout's specificity, and LAST IN THE FILE.

   🔑 BOTH halves of that sentence are load-bearing. Every rule these correct is
   stated at (0,2,0) in § 20 above, and a media query adds no specificity — so the
   base's bare-class versions lose on specificity and anything written up in § 19's
   blocks loses on SOURCE ORDER, § 20 being written after them. That is not a
   hypothetical: the ≤768 and ≤425 caps were placed there first and measured as
   completely inert.

   🔑 IT IS A REAL BREAK, NOT A TIDY-UP. Measured at 320px: signed out the row wanted
   317px in the 284px its gutter left it, so the credit badge — `flex:0 0 auto`, and
   with no text to ellipsise — was painted over the Log in button. Signed in with a
   long name it wanted 455px and Book now sat 62px OFF the right edge of the page,
   which the whole document then scrolled sideways to reach.

   ⚠️ The Book-now pill's padding is NOT one of these trims: the base's documented
   rule for `.pts-btn-small` is that it keeps its size at every width, because a
   narrower CTA on a phone reads as a different, lesser control. The space comes out
   of chrome — the gutter, the row gap, the credit plate and the customer's own name,
   which is what the shrinkable items are for.
   ========================================================================== */
@media (max-width:768px){
	.ptt-shell .pts-user-name{max-width:60px}
}

/* 🔑 THE CHIP'S NAME STAYS COMPACT TO 450, NOT 426 — BECAUSE A WHITE-LABELED BRAND IS
   ~50px WIDER THAN THE CREDIT PLATE THE BASE'S LADDER WAS MEASURED AGAINST. Same band,
   same cause and same fix as Kinetra's copy of this block: at 426 the ≤425 compaction
   below is released, the actions cluster jumps while the viewport gains one pixel, and
   with Remove Picktime Branding on the brand beside it is the logo + name lockup rather
   than the plate. Measured page overflow: 20px at 426, 16 at 430, 6 at 440, gone by 450
   — and 0 across the whole band when branded, which is why the release point is what is
   wrong rather than the ≤425 numbers.
   ⚠️ The NAME is the item that yields (the base's doctrine: the signed-in name gives way
   to the brand text, never the reverse, and never the Book-now pill's padding).
   ⚠️ After the ≤768 block on purpose — both match here, both are (0,2,0), and a media
   query adds no specificity, so only source order decides. */
@media (max-width:449.98px){
	.ptt-shell .pts-user-name{max-width:38px}
}

@media (max-width:425px){
	.ptt-shell{--ptt-gutter:10px}
	.ptt-shell .ptt-header-in{gap:10px}
	.ptt-shell .ptt-actions{gap:6px}
	.ptt-shell .pts-user-btn{gap:6px;padding:0 8px 0 5px}
	.ptt-shell .pts-user-btn .pts-avatar{width:26px;height:26px}
	.ptt-shell .pts-user-name{max-width:38px}
}

/* 🔑 BELOW 400 THE ACCOUNT CHIP IS THE AVATAR ALONE — the same call Pawmi makes at the
   same width. Signed in, this row is badge + account chip + Book now, and the badge is
   the one item that can neither ellipsise nor shrink, so something has to go; the
   customer's own name is the only candidate still one tap away (it heads the menu this
   button opens). The base caps the name instead, but the base's brand yields by
   ellipsising its own text — this one cannot, which is why that trade-off does not
   transfer. */
@media (max-width:399.98px){
	.ptt-shell .pts-user-name{display:none}
	.ptt-shell .pts-user-btn{gap:5px;padding:0 7px 0 5px}
}
