/* ============================================
   TATTOO TAMPA BAY — SITE-SPECIFIC STYLES
   Extends shared tokens/base/components.
   ============================================ */

/* =========================================================
   BUTTONS — primary & ghost (used across all pages)
   ========================================================= */

.tt-btn-primary,
.tt-btn-ghost {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-6);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: var(--track-button);
  border-radius: var(--radius-md);
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  line-height: var(--leading-none);
  transition: background var(--duration-fast) var(--ease-out),
              border-color var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out),
              transform var(--duration-fast) var(--ease-out),
              box-shadow var(--duration-fast) var(--ease-out);
}

.tt-btn-primary {
  background: var(--accent);
  color: var(--text-on-fill);
  border: 1px solid var(--accent);
}

.tt-btn-primary:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  transform: translateY(-1px);
  box-shadow: 0 8px 22px var(--accent-glow);
}

.tt-btn-primary:active {
  background: var(--accent-active);
  border-color: var(--accent-active);
  transform: translateY(0);
  box-shadow: none;
}

.tt-btn-ghost {
  background: transparent;
  color: var(--text-primary);
  border: 1px solid var(--border-strong);
}

.tt-btn-ghost:hover {
  border-color: var(--accent);
  color: var(--accent-text);
  transform: translateY(-1px);
}

/* =========================================================
   HEADER EXTENSIONS — CTA + mobile nav
   ========================================================= */

.btn-cta {
  display: none;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-5);
  background: var(--accent);
  color: var(--text-on-fill);
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: var(--track-button);
  border-radius: var(--radius-md);
  transition: background var(--duration-fast) var(--ease-out), transform var(--duration-fast) var(--ease-out);
}

.btn-cta:hover {
  background: var(--accent-hover);
  transform: translateY(-1px);
}

.btn-cta:active {
  background: var(--accent-active);
  transform: translateY(0);
}

@media (min-width: 720px) {
  .btn-cta { display: inline-flex; }
}

/* The bars stay 32×22; the padding carries the hit area to 44×44 (content-box,
   so the box is content + padding). It was 40×30 — over the 24px WCAG 2.5.8
   floor but under the 44px of 2.5.5 and every mobile platform guideline, on the
   one control that owns navigation below 1140px. */
.nav-toggle {
  display: inline-flex;
  flex-direction: column;
  justify-content: space-between;
  width: 32px;
  height: 22px;
  padding: 11px 6px;
  box-sizing: content-box;
}

.nav-toggle span {
  display: block;
  height: 2px;
  width: 100%;
  background: var(--text-primary);
  border-radius: var(--radius-pill);
  transition: transform var(--duration-normal) var(--ease-out), opacity var(--duration-normal) var(--ease-out);
}

.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(10px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-10px) rotate(-45deg); }

@media (min-width: 1140px) {
  .nav-toggle { display: none; }
}

.mobile-nav {
  position: fixed;
  inset: var(--header-h) 0 0 0;
  background: var(--bg);
  padding: var(--space-8) var(--section-px);
  z-index: 49;
  overflow-y: auto;
}

/* Once the desktop nav is on, the toggle is gone — so the panel must not be
   able to survive a resize and trap the page behind it. */
@media (min-width: 1140px) {
  .mobile-nav { display: none; }
}

/* Scroll lock lives with the panel state rather than in an inline style. */
body.is-nav-open { overflow: hidden; }

.mobile-nav nav {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-width: var(--max-width-narrow);
  margin: 0 auto;
}

.mobile-nav nav a {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 500;
  color: var(--text-primary);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--border);
  letter-spacing: var(--track-heading);
}

.mobile-nav nav a.is-active { color: var(--accent-text); }
.mobile-nav nav a:hover { color: var(--accent); }

/* Class-qualified so it outranks `.mobile-nav nav a` on specificity alone; this
   rule used to carry three !important flags to win a fight it had already won. */
.mobile-nav nav a.mobile-nav__cta {
  margin-top: var(--space-6);
  background: var(--accent);
  color: var(--text-on-fill);
  border-bottom: none;
  border-radius: var(--radius-md);
  text-align: center;
  padding: var(--space-4);
}

/* =========================================================
   SECTIONS — shared shell
   ========================================================= */

.tt-section {
  padding: var(--section-py) 0;
}

.tt-section--alt {
  background: var(--surface);
}

.tt-section__head {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  max-width: var(--max-width-narrow);
  margin: 0 auto var(--space-12);
  text-align: center;
}

.tt-section__head--left {
  text-align: left;
  margin-left: 0;
  margin-right: 0;
  max-width: var(--max-width);
}

.tt-section__title {
  font-family: var(--font-display);
  font-size: var(--text-4xl);
  font-weight: 700;
  letter-spacing: var(--track-title);
  color: var(--text-primary);
}

/* Unbounded ships no italic, so `font-style: italic` here was a browser-faked
   slant. Emphasis is carried the way the hero already carries it: the accent
   word breaks weight away from its neighbour — lighter here because the base is
   700, heavier in the hero because the base is 500 — plus the accent colour. */
.tt-section__title em {
  color: var(--accent-text);
  font-style: normal;
  font-weight: 500;
}

.tt-section__subtitle {
  font-size: var(--text-lg);
  color: var(--text-secondary);
  max-width: var(--max-width-text);
  margin-inline: auto;
  line-height: var(--leading-body);
}

.tt-section__head--left .tt-section__subtitle { margin-inline: 0; }

/* =========================================================
   HERO — split layout (text + featured photo panel)
   ========================================================= */

/* =========================================================
   HERO — Cinematic full-bleed (image as background, text overlaid)
   ========================================================= */

.tt-hero {
  position: relative;
  min-height: 100svh;
  background: var(--bg);
  overflow: hidden;
  isolation: isolate;
  display: flex;
  align-items: center;
}

/* Full-bleed image layer */
.tt-hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.tt-hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 60% 50%;
  display: block;
  transition: transform 12s var(--ease-out);
}

.tt-hero:hover .tt-hero__media img { transform: scale(1.03); }

/* Veil — light touch: image is already dark and rich.
   Desktop: subtle left wash for text legibility while keeping the
   "BIG / UNSEEN" graffiti readable as texture.
   Mobile: stronger bottom fade so text sits cleanly under the figure. */
.tt-hero__veil {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(180deg, transparent 65%, rgba(13, 13, 13, 0.95) 100%),
    linear-gradient(
      90deg,
      rgba(13, 13, 13, 0.6) 0%,
      rgba(13, 13, 13, 0.3) 28%,
      rgba(13, 13, 13, 0.05) 55%,
      transparent 75%
    );
}

@media (max-width: 1023px) {
  .tt-hero__veil {
    background:
      linear-gradient(
        180deg,
        rgba(13, 13, 13, 0.45) 0%,
        rgba(13, 13, 13, 0.25) 35%,
        rgba(13, 13, 13, 0.7) 65%,
        rgba(13, 13, 13, 0.98) 100%
      );
  }

  .tt-hero__media img { object-position: 65% 35%; }
}

/* Text overlay */
.tt-hero__text {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: var(--max-width);
  margin: 0 auto;
  padding: calc(var(--header-h) + var(--space-10)) var(--section-px) var(--space-10);
  display: flex;
  align-items: flex-end;
  min-height: 100svh;
}

@media (min-width: 1024px) {
  .tt-hero__text {
    align-items: center;
    padding-block: calc(var(--header-h) + var(--space-12)) var(--space-12);
  }
}

.tt-hero__text-inner {
  max-width: 560px;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.tt-hero__eyebrow {
  font-size: var(--text-xs);
  letter-spacing: var(--track-label-wide);
  text-transform: uppercase;
  color: var(--accent-text);
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  text-shadow: 0 1px 12px rgba(13, 13, 13, 0.8);
}

/* Small accent rule so the line reads as a label, not stray small text. */
.tt-hero__eyebrow::before {
  content: "";
  width: var(--space-8);
  height: 2px;
  background: var(--accent);
  flex: none;
}

.tt-hero__title {
  font-family: var(--font-display);
  font-size: clamp(2.75rem, 6vw, 5rem);
  font-weight: 500;
  line-height: 0.98;
  letter-spacing: var(--track-display);
  color: var(--hero-white);
  text-transform: uppercase;
  margin: 0;
  text-shadow: 0 2px 32px var(--scrim-soft);
}

.tt-hero__title em {
  font-style: normal;
  font-weight: 700;
  display: block;
  color: var(--hero-white-peak);
}

/* Was clamp(1rem, 1.1vw, 1.15rem), whose middle term only clears the 1rem floor
   past a 1454px viewport — so the hero's supporting line rendered at 16px on
   virtually every screen, a step *smaller* than the --text-lg subtitle under
   every other section title. The most important supporting copy on the site was
   the smallest. */
.tt-hero__sub {
  font-size: var(--text-lg);
  color: var(--hero-white-soft);
  line-height: var(--leading-body);
  max-width: 44ch;
  opacity: 1;
  text-shadow: 0 1px 14px rgba(13, 13, 13, 0.75);
}

.tt-hero__cta {
  width: fit-content;
  margin-top: var(--space-2);
}

.tt-hero__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid rgba(255, 255, 255, 0.18);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: var(--track-label-wide);
  color: var(--text-secondary);
  font-weight: 500;
}

.tt-hero__meta > span:not([aria-hidden]) {
  color: var(--text-primary);
  opacity: 0.85;
}

/* =========================================================
   SECTION KICKER
   Legacy name: this is the only surviving piece of the old manifesto block,
   and every page uses it as the label above a section title.
   ========================================================= */

/* The section eyebrow, in the label register — the same treatment as the hero's,
   minus its accent rule. It was called `tt-manifesto__num` on thirteen section
   heads across six pages: a numeral slot from a manifesto component that no
   longer exists, printing words, in Unbounded at --text-sm in sentence case with
   --track-label applied. Same defect as the carousel labels, ten instances wider. */
.tt-section__label {
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  color: var(--accent-text);
  letter-spacing: var(--track-label);
}

/* =========================================================
   PORTFOLIO GRID
   ========================================================= */

.tt-grid {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(2, 1fr);
}

@media (min-width: 640px) { .tt-grid { grid-template-columns: repeat(3, 1fr); gap: var(--space-4); } }
@media (min-width: 1024px) { .tt-grid { grid-template-columns: repeat(4, 1fr); } }

.tt-tile {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-md);
  background: var(--surface);
  aspect-ratio: 1 / 1;
  cursor: zoom-in;
  display: block;
}

.tt-tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--duration-slow) var(--ease-out), filter var(--duration-slow) var(--ease-out);
  display: block;
}

.tt-tile:hover img {
  transform: scale(1.06);
  filter: brightness(0.85);
}

.tt-tile__caption {
  position: absolute;
  inset: auto 0 0 0;
  padding: var(--space-6) var(--space-4) var(--space-4);
  background: linear-gradient(180deg, transparent, var(--scrim-strong));
  color: var(--text-primary);
  font-size: var(--text-sm);
  letter-spacing: var(--track-button);
  text-transform: uppercase;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--duration-normal) var(--ease-out), transform var(--duration-normal) var(--ease-out);
}

.tt-tile:hover .tt-tile__caption,
.tt-tile:focus-visible .tt-tile__caption {
  opacity: 1;
  transform: translateY(0);
}

.tt-tile__artist {
  display: inline-block;
  margin-top: var(--space-1);
  font-size: var(--text-xs);
  color: var(--accent-text);
  letter-spacing: var(--track-button);
}

/* The three carousel rows are siblings at --space-12; this button closes the
   whole section. At the same interval it read as a fourth row, so it takes the
   block-level step instead. */
.tt-portfolio-cta {
  display: flex;
  justify-content: center;
  margin-top: var(--space-block);
}

/* =========================================================
   PROCESS — 4 numbered steps
   ========================================================= */

/* A sequence, not four categories. The card boxes are gone: this system says
   depth comes from layering and hairlines, and four equal --surface rectangles
   were a borrowed template that made the order decorative — carried only by the
   numerals — instead of structural.

   What connects the steps now is one hairline per step, all at the same y. In a
   four-up row they read as a single ruled staff broken into measures; when the
   grid wraps they simply become the rule above each row. Deliberately not one
   continuous connector: a snaking line across a 2x2 wrap is where this kind of
   thing ends up hanging in mid-air.

   The column gap is tighter than the row gap so the eye reads across before it
   reads down — which is the actual step order. */
.tt-process {
  display: grid;
  grid-template-columns: 1fr;
  column-gap: var(--space-6);
  row-gap: var(--space-10);
  counter-reset: step;
}

@media (min-width: 720px) { .tt-process { grid-template-columns: repeat(2, 1fr); } }

/* Four columns used to start at 1024px, where 32px of card padding on both sides
   cut a ~222px column to a ~158px measure — about 20 characters per line. The
   cards are gone now, so the same column would read at ~30 characters; four
   columns at 1140px give ~34. The breakpoint stays here anyway, because 1140px is
   already the width at which this site declares the desktop row fits (see the
   nav): one definition of desktop, not two. */
@media (min-width: 1140px) { .tt-process { grid-template-columns: repeat(4, 1fr); } }

/* No hover: a step is an <li>, not a control. It used to lift 3px and take an
   accent border, promising a click that never existed. */
.tt-step {
  counter-increment: step;
  border-top: 1px solid var(--border);
  padding-top: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* The numeral is generated rather than typed. The `step` counter was already
   reset on .tt-process but never incremented or printed — dead code sitting
   beside four hardcoded literals that a markup edit could silently desync.
   The <ol> carries the order for assistive tech, so this glyph is a decorative
   duplicate, which is exactly what generated content is for. */
.tt-step::before {
  content: counter(step, decimal-leading-zero);
  font-family: var(--font-display);
  font-size: var(--text-4xl);
  font-weight: 700;
  color: var(--accent);
  line-height: var(--leading-none);
}

/* Was --text-lg, the smallest heading on the page. Dropping the card padding
   also gave the body copy its measure back: ~34 characters per line at 1140px
   instead of ~25 inside a 32px-padded box. */
.tt-step__title {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 600;
  color: var(--text-primary);
  letter-spacing: var(--track-heading);
  line-height: var(--leading-snug);
}

.tt-step__text {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: var(--leading-body);
}

/* =========================================================
   FAQ — accordion
   ========================================================= */

.tt-faq {
  max-width: var(--max-width-narrow);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.tt-faq-item {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  overflow: hidden;
  transition: border-color var(--duration-normal) var(--ease-out);
}

.tt-faq-item[open] { border-color: var(--accent); }

.tt-faq-item summary {
  cursor: pointer;
  padding: var(--space-5) var(--space-6);
  font-family: var(--font-display);
  font-size: var(--text-base);
  font-weight: 500;
  letter-spacing: var(--track-heading);
  color: var(--text-primary);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-4);
  list-style: none;
}

.tt-faq-item summary::-webkit-details-marker { display: none; }

/* The thin plus needs weight 300, which the display face does not carry —
   it was silently snapping to 500. The body face does, so use it: this is a
   UI glyph, not a heading. */
.tt-faq-item summary::after {
  content: "+";
  font-family: var(--font-body);
  font-size: var(--text-2xl);
  color: var(--accent-text);
  font-weight: 300;
  transition: transform var(--duration-normal) var(--ease-out);
  flex-shrink: 0;
}

.tt-faq-item[open] summary::after { transform: rotate(45deg); }

.tt-faq-item__body {
  padding: 0 var(--space-6) var(--space-6);
  color: var(--text-secondary);
  font-size: var(--text-base);
  line-height: var(--leading-body);
}

.tt-faq-item__body p + p { margin-top: var(--space-3); }
.tt-faq-item__body ul { margin-top: var(--space-2); padding-left: var(--space-5); }
.tt-faq-item__body li { list-style: disc; margin-top: var(--space-1); }

/* =========================================================
   BLOG
   ========================================================= */

.tt-blog-grid {
  display: grid;
  gap: var(--space-8);
  grid-template-columns: 1fr;
}

@media (min-width: 720px) { .tt-blog-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .tt-blog-grid { grid-template-columns: repeat(3, 1fr); } }

.tt-blog-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-6);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  transition: border-color var(--duration-normal) var(--ease-out), transform var(--duration-normal) var(--ease-out);
  height: 100%;
  text-decoration: none;
  color: inherit;
}

.tt-blog-card:hover {
  border-color: var(--accent);
  transform: translateY(-3px);
}

.tt-blog-card__title {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 600;
  letter-spacing: var(--track-heading);
  color: var(--text-primary);
  line-height: var(--leading-snug);
}

.tt-blog-card__excerpt {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: var(--leading-body);
  flex: 1;
}

.tt-blog-card__more {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--accent-text);
  letter-spacing: var(--track-button);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.tt-blog-card__more::after {
  content: "→";
  transition: transform var(--duration-normal) var(--ease-out);
}

.tt-blog-card:hover .tt-blog-card__more::after { transform: translateX(4px); }

/* =========================================================
   ARTICLE — single blog post
   ========================================================= */

.tt-article {
  --article-measure: 44rem;   /* comfortable line length for body copy */
  --article-wide: 58rem;      /* tables and media break out to this */
  max-width: var(--article-wide);
  margin: 0 auto;
  padding: var(--section-py) var(--section-px);
}

.tt-article > .tt-article__byline,
.tt-article__title {
  max-width: var(--article-measure);
  margin-left: auto;
  margin-right: auto;
}

.tt-article__byline {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin-top: calc(var(--space-8) * -1 + var(--space-2));
  margin-bottom: var(--space-10);
}

.tt-article__title {
  font-family: var(--font-display);
  font-size: var(--text-4xl);
  font-weight: 700;
  letter-spacing: var(--track-title);
  line-height: var(--leading-tight);
  color: var(--text-primary);
  margin-bottom: var(--space-8);
  text-wrap: balance;
}

/* Text keeps a readable measure; wide blocks (tables, media) break out of it. */
.tt-article__body {
  color: var(--text-primary);
  font-size: var(--text-lg);
  line-height: var(--leading-relaxed);
  display: grid;
  grid-template-columns:
    1fr min(var(--article-measure), 100%) 1fr;
}

.tt-article__body > * {
  grid-column: 2;
}

.tt-article__body > .tt-table-wrap,
.tt-article__body > figure,
.tt-article__body > img {
  grid-column: 1 / -1;
  width: 100%;
}

/* Opening paragraph carries the answer, so give it presence. */
.tt-article__body > p:first-of-type {
  font-size: clamp(1.1875rem, 1.05rem + 0.55vw, 1.4375rem);
  line-height: var(--leading-body);
  color: var(--text-primary);
  padding-bottom: var(--space-6);
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--space-8);
}

.tt-article__body h2 {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 700;
  letter-spacing: var(--track-heading);
  margin-top: var(--space-16);
  margin-bottom: var(--space-5);
  color: var(--text-primary);
  text-wrap: balance;
}

.tt-article__body h2::before {
  content: "";
  display: block;
  width: var(--space-10);
  height: 2px;
  background: var(--accent);
  opacity: 0.5;
  margin-bottom: var(--space-5);
}

.tt-article__body h3 {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 600;
  margin-top: var(--space-8);
  margin-bottom: var(--space-3);
  color: var(--text-primary);
}

.tt-article__body p { margin-bottom: var(--space-5); color: var(--text-primary); }
.tt-article__body p + p { margin-top: 0; }

.tt-article__body ul, .tt-article__body ol {
  margin: var(--space-4) 0 var(--space-6);
  padding-left: var(--space-6);
  color: var(--text-primary);
}

.tt-article__body ul li { list-style: disc; margin-bottom: var(--space-2); }
.tt-article__body ol li { list-style: decimal; margin-bottom: var(--space-2); }

.tt-article__body strong { color: var(--text-primary); font-weight: 600; }
/* Colour, not slant: no article uses <em> today, so the italic axis is not
   loaded and `font-style: italic` would only ever be a faked oblique. Archivo
   does have a real italic — to use it, add `Archivo:ital,wght@1,300..700` to
   the font request in every page head plus _src/scripts/build_blog.py, then
   restore `font-style: italic` here. */
.tt-article__body em { color: var(--accent-text); font-style: normal; }

.tt-article__body a {
  color: var(--accent-text);
  text-decoration: underline;
  text-decoration-color: var(--accent-subtle);
  text-underline-offset: 3px;
  transition: text-decoration-color var(--duration-fast) var(--ease-out);
}
.tt-article__body a:hover { text-decoration-color: var(--accent); }

.tt-article__body hr {
  border: 0;
  border-top: 1px solid var(--border);
  margin: var(--space-12) 0;
}

.tt-article__body code {
  font-family: var(--font-mono);
  font-size: 0.92em;
  background: var(--surface-alt);
  padding: 2px 6px;
  border-radius: var(--radius-sm);
}

/* --- Reading progress --------------------------------------------------- */

.tt-progress {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  z-index: 200;
  pointer-events: none;
}

.tt-progress span {
  display: block;
  height: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  will-change: transform;
}

/* --- Breadcrumbs --------------------------------------------------------- */

.tt-breadcrumbs {
  max-width: var(--article-measure);
  margin: 0 auto var(--space-6);
  display: flex;
  gap: var(--space-2);
  align-items: center;
  font-size: var(--text-xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-secondary);
}

.tt-breadcrumbs a {
  color: var(--text-secondary);
  text-decoration: none;
  transition: color var(--duration-fast) var(--ease-out);
}

.tt-breadcrumbs a:hover { color: var(--accent-text); }

/* --- Table of contents --------------------------------------------------- */

.tt-toc {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-6) var(--space-6) var(--space-5);
  margin: 0 0 var(--space-12);
}

.tt-toc__label {
  font-size: var(--text-xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--accent-text);
  margin-bottom: var(--space-4);
}

.tt-toc ol {
  counter-reset: toc;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-2);
  list-style: none;
}

.tt-toc li {
  counter-increment: toc;
  list-style: none;
  margin: 0;
  font-size: var(--text-base);
  line-height: 1.45;
  display: flex;
  gap: var(--space-3);
}

.tt-toc li::before {
  content: counter(toc, decimal-leading-zero);
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  flex: none;
}

.tt-toc a {
  color: var(--text-secondary);
  text-decoration: none;
  transition: color var(--duration-fast) var(--ease-out);
}

.tt-toc a:hover { color: var(--accent-text); }

/* Anchored headings should not hide under anything when jumped to.
   The sticky header is taller than the old space-8 offset, so a TOC jump
   used to land with the heading tucked behind it. */
.tt-article__body h2 { scroll-margin-top: calc(var(--header-h) + var(--space-4)); }

/* --- Tables ------------------------------------------------------------ */

.tt-table-wrap {
  margin: var(--space-10) 0;
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  -webkit-overflow-scrolling: touch;
}

.tt-article__body table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-base);
  line-height: var(--leading-body);
  min-width: 34rem;
}

.tt-article__body thead th {
  background: var(--surface-alt);
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--accent-text);
  text-align: left;
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border-strong);
  white-space: nowrap;
}

.tt-article__body tbody td {
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
  vertical-align: top;
  color: var(--text-primary);
}

.tt-article__body tbody tr:last-child td { border-bottom: 0; }

/* First column is the label the row is about — separated by weight, not colour.
   The zebra stripe that used to sit here was white at 1.5%, which on --surface
   is about one step of #18 against #16: invisible, and redundant besides, since
   every row already carries a --border hairline. The other cells used to dim to
   #C9C4BE, a fourth text tier the system does not have — and the data in a
   table is what the reader came for, so it reads in bone like every other value. */
.tt-article__body tbody td:first-child {
  font-weight: 600;
}

/* --- FAQ ---------------------------------------------------------------- */

.tt-faq {
  display: grid;
  gap: var(--space-3);
}

/* No accent stripe: the surface layer, the hairline and the question set in the
   display face already separate one answer from the next. */
.tt-faq p {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-5) var(--space-6);
  margin-bottom: 0;
  font-size: var(--text-base);
  color: var(--text-secondary);
}

.tt-faq p > strong:first-child {
  display: block;
  font-family: var(--font-display);
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: var(--space-2);
  line-height: 1.4;
}

/* --- Lists: numbered lists read as steps -------------------------------- */

.tt-article__body > ol {
  counter-reset: step;
  list-style: none;
  padding: 0;
  margin: var(--space-8) 0;
  display: grid;
  gap: var(--space-3);
}

.tt-article__body > ol > li {
  counter-increment: step;
  list-style: none;
  margin: 0;
  position: relative;
  padding: var(--space-5) var(--space-6) var(--space-5) calc(var(--space-16) + var(--space-2));
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-size: var(--text-base);
  line-height: var(--leading-body);
  color: var(--text-secondary);
}

.tt-article__body > ol > li::before {
  content: counter(step, decimal-leading-zero);
  position: absolute;
  left: var(--space-6);
  top: var(--space-5);
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 700;
  line-height: var(--leading-none);
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

.tt-article__body > ol > li > strong:first-child {
  color: var(--text-primary);
  display: inline;
}

.tt-article__body ul > li::marker { color: var(--accent); }
.tt-article__body li > strong:first-child { color: var(--text-primary); }

/* --- Callout: use a blockquote for a verdict worth stopping at ----------
   The accent wash carries it; there is no left stripe. This is the only
   blockquote rule, so a quote reads the same at any nesting depth. */

.tt-article__body blockquote {
  background: var(--accent-subtle);
  border: 0;
  border-radius: var(--radius-md);
  padding: var(--space-6);
  margin: var(--space-10) 0;
  font-style: normal;
  font-size: clamp(1.0625rem, 1rem + 0.35vw, 1.25rem);
  line-height: var(--leading-body);
  color: var(--text-primary);
}

.tt-article__body blockquote p:last-child { margin-bottom: 0; }

/* --- Read next ---------------------------------------------------------- */

.tt-readnext {
  margin-top: var(--space-16);
  padding-top: var(--space-10);
  border-top: 1px solid var(--border);
}

.tt-readnext__title {
  font-family: var(--font-display);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--accent-text);
  margin-bottom: var(--space-6);
}

.tt-readnext .tt-blog-grid {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: 1fr;
}

@media (min-width: 720px) {
  .tt-readnext .tt-blog-grid { grid-template-columns: repeat(3, 1fr); }
}

.tt-readnext .tt-blog-card__title {
  font-size: var(--text-base);
  line-height: 1.35;
}

/* The closing CTA card, used by every page that ends on an ask — six section-based
   pages and eleven articles. It was called `tt-article__cta`, a name that claimed
   an ownership it never had: two thirds of its users are not articles.

   It carries no top margin of its own. Inside a section, --section-py already
   supplies the separation and adding --space-16 on top of it stacked two intervals;
   only the article layout needs the margin, to clear its read-next block. */
.tt-cta-card {
  padding: var(--space-10);
  background: var(--surface);
  border-radius: var(--radius-lg);
  border: 1px solid var(--accent-subtle);
  text-align: center;
}

.tt-article .tt-cta-card { margin-top: var(--space-16); }

.tt-cta-card h3 {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  margin-bottom: var(--space-3);
}

.tt-cta-card p {
  color: var(--text-secondary);
  margin-bottom: var(--space-6);
}

/* =========================================================
   FOOTER
   ========================================================= */

/* Every page's last block already owns its closing space: a section's
   --section-py, the article wrapper's padding, or — on the home page — the map,
   which is deliberately flush. So the footer adds no margin of its own.
   `margin-top: auto` used to sit here as a sticky-footer idiom, but the footer is
   wrapped in #footer-slot rather than being a direct flex child of body, so it
   computed to 0 and did nothing. Short pages are already held down by
   `main { flex: 1 }` in base.css. */
.site-footer {
  background: var(--bg);
  border-top: 1px solid var(--border);
}

.tt-footer__inner {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 var(--section-px);
}

.tt-footer__top {
  padding: var(--space-12) 0;
  border-bottom: 1px solid var(--border);
}

.tt-footer__top .tt-footer__inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  align-items: flex-start;
  justify-content: space-between;
}

@media (min-width: 800px) {
  .tt-footer__top .tt-footer__inner { flex-direction: row; align-items: center; }
}

.tt-footer__pitch {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  color: var(--text-primary);
  letter-spacing: var(--track-heading);
  margin-top: var(--space-2);
  line-height: 1.2;
}

.tt-footer__pitch span { color: var(--text-secondary); display: block; font-weight: 400; }

.tt-footer__cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-8);
  background: var(--accent);
  color: var(--text-on-fill);
  font-weight: 600;
  border-radius: var(--radius-md);
  font-size: var(--text-base);
  letter-spacing: var(--track-button);
  transition: background var(--duration-fast) var(--ease-out), transform var(--duration-fast) var(--ease-out);
  flex-shrink: 0;
}

.tt-footer__cta:hover {
  background: var(--accent-hover);
  transform: translateY(-2px);
}

.tt-footer__mid {
  padding: var(--space-16) 0 var(--space-12);
  border-bottom: 1px solid var(--border);
}

.tt-footer__mid .tt-footer__inner {
  display: grid;
  gap: var(--space-8);
  grid-template-columns: 1fr 1fr;
}

@media (min-width: 720px) {
  .tt-footer__mid .tt-footer__inner { grid-template-columns: repeat(4, 1fr); }
}

.tt-footer__heading {
  font-family: var(--font-display);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--accent-text);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  margin-bottom: var(--space-4);
}

.tt-footer__col ul { display: flex; flex-direction: column; gap: var(--space-2); }
.tt-footer__col a, .tt-footer__col li {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  transition: color var(--duration-fast) var(--ease-out);
}
.tt-footer__col a:hover { color: var(--accent-text); }

.tt-footer__legal {
  padding: var(--space-6) 0;
}

/* Was --text-dim (2.64:1) — the most-repeated text on the site, and the last
   readable-text use of a tier that cannot pass AA at any lightness. See
   DESIGN.md, The Dim-Is-Not-Text Rule. */
.tt-footer__legal .tt-footer__inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  font-size: var(--text-xs);
  color: var(--text-secondary);
}

@media (min-width: 720px) {
  .tt-footer__legal .tt-footer__inner {
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
  }
}

.tt-footer__legal-text { display: flex; flex-direction: column; gap: var(--space-1); }
.tt-footer__legal-text a { color: var(--accent-text); }
.tt-footer__legal-text a:hover { color: var(--accent); }
/* Three items now — the two legal pages and the Instagram handle. Laid out
   rather than left to collapse into one run-on line of inline anchors. */
.tt-footer__legal-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-4);
}

.tt-footer__legal-links a { color: var(--text-secondary); }
.tt-footer__legal-links a:hover { color: var(--accent-text); }

/* =========================================================
   MEET — homepage artist intro (with portrait photos)
   ========================================================= */

.tt-meet {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: 1fr;
}

@media (min-width: 760px) {
  .tt-meet { grid-template-columns: repeat(2, 1fr); gap: var(--space-8); }
}

.tt-meet__card {
  position: relative;
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--surface);
  border: 1px solid var(--border);
  transition: border-color var(--duration-normal) var(--ease-out), transform var(--duration-normal) var(--ease-out);
}

.tt-meet__card:hover {
  border-color: var(--accent);
  transform: translateY(-4px);
}

/* An <a> so the portrait keeps a large tap target now that the card itself
   is a container rather than one big link. */
.tt-meet__photo {
  display: block;
  position: relative;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: var(--surface-alt);
}

.tt-meet__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 700ms var(--ease-out), filter 700ms var(--ease-out);
  filter: grayscale(0.1) brightness(0.95);
}

.tt-meet__card:hover .tt-meet__photo img {
  transform: scale(1.04);
  filter: grayscale(0) brightness(1);
}

.tt-meet__photo::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, transparent 55%, rgba(13, 13, 13, 0.55));
  pointer-events: none;
}

.tt-meet__body {
  padding: var(--space-8);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.tt-meet__style {
  font-size: var(--text-xs);
  color: var(--accent-text);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  font-weight: 600;
}

.tt-meet__name {
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: 700;
  letter-spacing: var(--track-title);
  color: var(--text-primary);
  line-height: 1.05;
}

.tt-meet__bio {
  font-size: var(--text-base);
  color: var(--text-secondary);
  line-height: var(--leading-body);
}

.tt-meet__link {
  margin-top: var(--space-3);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--accent-text);
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: var(--track-button);
  text-transform: uppercase;
}

.tt-meet__link::after {
  content: "→";
  transition: transform var(--duration-normal) var(--ease-out);
}

.tt-meet__card:hover .tt-meet__link::after { transform: translateX(6px); }

/* Booking first, portfolio second: the primary action is a filled button, the
   portfolio stays the quiet text link it already was. */
.tt-meet__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
  margin-top: var(--space-4);
}

.tt-meet__actions .tt-meet__link { margin-top: 0; }

.tt-meet__book svg {
  width: 18px;
  height: 18px;
  flex: none;
}

/* =========================================================
   BIO — full artist intro on portfolio pages
   ========================================================= */

.tt-bio {
  display: grid;
  gap: var(--space-8);
  grid-template-columns: 1fr;
  align-items: center;
  margin-bottom: var(--space-16);
}

@media (min-width: 800px) {
  .tt-bio {
    grid-template-columns: 5fr 7fr;
    gap: var(--space-12);
  }
}

.tt-bio__photo {
  position: relative;
  aspect-ratio: 4 / 5;
  max-width: 480px;
  width: 100%;
  margin: 0 auto;
  overflow: hidden;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  background: var(--surface-alt);
  isolation: isolate;
}

.tt-bio__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  position: relative;
  z-index: 0;
}

.tt-bio__photo::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(160deg, var(--accent-subtle), transparent 45%);
  pointer-events: none;
  mix-blend-mode: screen;
  opacity: 0.6;
}

.tt-bio__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.tt-bio__style {
  font-size: var(--text-sm);
  color: var(--accent-text);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  font-weight: 600;
}

.tt-bio__name {
  font-family: var(--font-display);
  font-size: clamp(var(--text-3xl), 5vw, var(--text-5xl));
  font-weight: 700;
  letter-spacing: var(--track-display);
  line-height: 0.95;
  color: var(--text-primary);
}

.tt-bio__lead {
  font-size: var(--text-lg);
  color: var(--text-primary);
  line-height: var(--leading-body);
  max-width: 56ch;
}

.tt-bio__text {
  font-size: var(--text-base);
  color: var(--text-secondary);
  line-height: var(--leading-body);
  max-width: 56ch;
}

.tt-bio__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-1);
}

.tt-bio__tag {
  display: inline-flex;
  align-items: center;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  color: var(--text-secondary);
  letter-spacing: var(--track-button);
  text-transform: uppercase;
}

.tt-bio__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

/* =========================================================
   CAROUSEL — horizontal scroll-snap with arrow nav
   ========================================================= */

.tt-carousels {
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
}

.tt-carousel {
  position: relative;
}

.tt-carousel__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-6);
}

/* The label register, not the display face — Archivo 600 at --text-xs, uppercase,
   the same treatment as the hero eyebrow. It used to be Unbounded at --text-sm in
   sentence case *with* label tracking: letter-spaced like a label, sized and cased
   like prose, which is neither of the two registers this system has. */
.tt-carousel__label {
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  color: var(--accent-text);
  letter-spacing: var(--track-label);
  margin-bottom: var(--space-2);
}

.tt-carousel__title {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 700;
  letter-spacing: var(--track-title);
  color: var(--text-primary);
  line-height: var(--leading-tight);
}

.tt-carousel__nav {
  display: flex;
  gap: var(--space-2);
  flex-shrink: 0;
}

/* On a phone the two arrows are unshrinkable and reserve 96px of a ~335px row —
   29% of the width — for a control that repeats the swipe the section subtitle
   already teaches. That squeezed the row title into a ~223px column, wrapping a
   24px display line to three or four lines. Below the tablet breakpoint the
   title takes the full width back. Tiles stay focusable, so tabbing still
   scrolls the track without them. */
@media (max-width: 719px) {
  .tt-carousel__nav { display: none; }
}

.tt-carousel__btn {
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  color: var(--text-primary);
  background: transparent;
  cursor: pointer;
  font-size: var(--text-lg);
  font-family: inherit;
  transition: border-color var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out),
              transform var(--duration-fast) var(--ease-out),
              opacity var(--duration-fast) var(--ease-out);
}

.tt-carousel__btn:hover:not(:disabled) {
  border-color: var(--accent);
  color: var(--accent-text);
  transform: translateY(-1px);
}

.tt-carousel__btn:disabled {
  opacity: 0.25;
  cursor: default;
}

.tt-carousel__track {
  display: flex;
  gap: var(--space-3);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-left: var(--section-px);
  padding: 0 var(--section-px) var(--space-3);
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.tt-carousel__track::-webkit-scrollbar { display: none; }

/* The "Loading…" placeholder that used to sit here is gone with the client-side
   render: the tiles are in the markup now, so there is nothing to wait for — and
   the placeholder's real behaviour was to sit there forever whenever the script
   failed to run at all. */

.tt-carousel__track > .tt-tile {
  flex: 0 0 auto;
  width: clamp(220px, 36vw, 340px);
  scroll-snap-align: start;
}

@media (min-width: 1200px) {
  .tt-carousel__track > .tt-tile { width: clamp(280px, 26vw, 380px); }
}

/* =========================================================
   PHOTOSWIPE OVERRIDES (lightbox)
   ========================================================= */

.pswp__bg { background: rgba(13, 13, 13, 0.97) !important; }

/* =========================================================
   HEADER SOCIAL ICONS — sit next to the WhatsApp CTA
   ========================================================= */

.header-socials {
  display: none;
  align-items: center;
  gap: var(--space-2);
}

@media (min-width: 720px) {
  .header-socials { display: inline-flex; }
}

.header-social {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  color: var(--text-secondary);
  transition: color var(--duration-fast) var(--ease-out),
              border-color var(--duration-fast) var(--ease-out),
              transform var(--duration-fast) var(--ease-out),
              background var(--duration-fast) var(--ease-out);
}

.header-social svg { width: 18px; height: 18px; display: block; }

.header-social:hover {
  color: var(--text-primary);
  border-color: var(--accent);
  background: var(--accent-subtle);
  transform: translateY(-1px);
}

/* =========================================================
   HERO — CTA pair (primary + ghost)
   ========================================================= */

.tt-hero__ctas {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-2);
}

.tt-hero__cta--ghost {
  background: rgba(255, 255, 255, 0.06);
  color: var(--hero-white);
  border: 1px solid rgba(255, 255, 255, 0.25);
  backdrop-filter: blur(6px);
}

.tt-hero__cta--ghost:hover {
  background: rgba(255, 255, 255, 0.12);
  border-color: var(--accent);
}

/* =========================================================
   CONTACT CARD — phone / email / address stacked
   ========================================================= */

.tt-contact-card {
  max-width: 640px;
  margin: 0 auto;
  padding: var(--space-8);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.tt-contact-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.tt-contact-line {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-3);
  border-radius: var(--radius-md);
  text-decoration: none;
  color: inherit;
  transition: background var(--duration-fast) var(--ease-out),
              transform var(--duration-fast) var(--ease-out);
}

.tt-contact-line:not(.tt-contact-line--static):hover {
  background: var(--surface-alt);
  transform: translateX(2px);
}

.tt-contact-line__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-pill);
  background: var(--accent-subtle);
  color: var(--accent-text);
  flex-shrink: 0;
}

.tt-contact-line__icon svg { width: 20px; height: 20px; display: block; }

.tt-contact-line__value {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  color: var(--text-primary);
  word-break: break-word;
  flex: 1;
}

/* No hint slot. It used to caption each row — "Email" beside an email address,
   "Location" beside a city name — and the brand-coloured swatch already names the
   channel besides. The one caption that did carry information, "WhatsApp" beside a
   bare number, is now a way-chip instead: it says the same thing and is tappable.

   A ways row lives inside the same list item as the value it belongs to, so the
   list's own --space-2 gap keeps separating channels rather than separating a value
   from its own chips. */
.tt-contact-line + .tt-ways { margin-top: var(--space-1); }

.tt-contact-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.tt-contact-actions > a {
  flex: 1 1 200px;
  justify-content: center;
}

/* =========================================================
   FAB — sticky pulsing booking button + popup
   ========================================================= */

.tt-fab {
  position: fixed;
  right: clamp(14px, 3vw, 28px);
  bottom: clamp(14px, 3vw, 28px);
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-3);
}

/* The FAB outranks every layer, including the open mobile panel — so it
   steps aside while the panel owns the screen. */
body.is-nav-open .tt-fab { display: none; }

.tt-fab__btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 60px;
  height: 60px;
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: var(--text-on-fill);
  border: none;
  cursor: pointer;
  padding: 0;
  box-shadow: 0 10px 24px var(--accent-glow),
              0 3px 8px rgba(0, 0, 0, 0.35);
  transition: transform var(--duration-fast) var(--ease-out),
              background var(--duration-fast) var(--ease-out);
  overflow: visible;
}

.tt-fab__btn:hover { background: var(--accent-hover); transform: translateY(-2px); }
.tt-fab__btn:active { background: var(--accent-active); }

.tt-fab__text {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 9px;
  letter-spacing: var(--track-heading);
  text-transform: lowercase;
  line-height: var(--leading-none);
  text-align: center;
  padding: 0 4px;
  transition: opacity var(--duration-fast) var(--ease-out),
              transform var(--duration-fast) var(--ease-out);
}

.tt-fab__text > span { display: block; white-space: nowrap; }

.tt-fab__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: opacity var(--duration-fast) var(--ease-out),
              transform var(--duration-fast) var(--ease-out);
}

.tt-fab__icon svg { width: 22px; height: 22px; display: block; }

.tt-fab__icon--close {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 22px;
  height: 22px;
  opacity: 0;
  transform: rotate(-45deg) scale(0.8);
}

.tt-fab.is-open .tt-fab__text {
  opacity: 0;
  transform: scale(0.85);
}

.tt-fab.is-open .tt-fab__icon--close {
  opacity: 1;
  transform: rotate(0) scale(1);
}

.tt-fab__pulse {
  position: absolute;
  inset: -4px;
  border-radius: var(--radius-pill);
  background: var(--accent);
  z-index: -1;
  opacity: 0.55;
  animation: tt-fab-pulse 2s var(--ease-out) infinite;
}

.tt-fab__pulse::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--accent);
  opacity: 0.4;
  animation: tt-fab-pulse 2s var(--ease-out) infinite;
  animation-delay: 0.6s;
}

@keyframes tt-fab-pulse {
  0%   { transform: scale(1);    opacity: 0.55; }
  70%  { transform: scale(1.45); opacity: 0;    }
  100% { transform: scale(1.45); opacity: 0;    }
}

.tt-fab.is-open .tt-fab__pulse { display: none; }

@media (prefers-reduced-motion: reduce) {
  .tt-fab__pulse, .tt-fab__pulse::after { animation: none; }

  /* The system's own rule — disable decorative motion under this setting — was only
     half implemented: the pulse was guarded and roughly twenty transitions were not.

     Timing goes to zero rather than transforms being blanket-disabled, because a
     handful of transforms encode state rather than decorate: the hamburger rotating
     into an X, the FAQ marker turning + into x, the reading-progress bar. Killing
     those would remove information, not motion. Durations are safe to zero out
     wholesale, and a blanket rule is the only version that also covers the next
     transition someone adds. !important is the accepted idiom here — it has to
     outrank the component that declared the transition. */
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
  }

  /* These three are neutralised outright, not merely un-timed: an instant 3–6% image
     jump under the cursor is precisely the sensation the setting asks us to avoid,
     whereas a 1px button lift arriving instantly is imperceptible. Colour and border
     changes are untouched throughout — they are what carries the affordance once the
     movement is gone, which is what The 4px Lift Ceiling was written for. */
  .tt-hero:hover .tt-hero__media img,
  .tt-tile:hover img,
  .tt-meet__card:hover .tt-meet__photo img {
    transform: none;
  }
}

.tt-fab__popup {
  width: min(380px, calc(100vw - 32px));
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.55),
              0 4px 12px rgba(0, 0, 0, 0.35);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  animation: tt-fab-popup-in 200ms var(--ease-out);
  transform-origin: bottom right;
}

@keyframes tt-fab-popup-in {
  from { opacity: 0; transform: translateY(8px) scale(0.96); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.tt-fab__popup[hidden] { display: none; }

.tt-fab__popup-title {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  color: var(--text-primary);
  letter-spacing: var(--track-heading);
  font-weight: 600;
}

.tt-fab__popup-sub {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  line-height: 1.5;
}

.tt-fab__channels {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.tt-fab__channel {
  display: grid;
  grid-template-columns: 40px 1fr auto;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--surface-alt);
  text-decoration: none;
  color: inherit;
  transition: border-color var(--duration-fast) var(--ease-out),
              transform var(--duration-fast) var(--ease-out),
              background var(--duration-fast) var(--ease-out);
}

.tt-fab__channel:hover {
  border-color: var(--accent);
  transform: translateX(2px);
  background: var(--surface-hover);
}

.tt-fab__channel-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-pill);
  background: var(--accent-subtle);
  color: var(--accent-text);
}

.tt-fab__channel-icon svg { width: 20px; height: 20px; display: block; }

.tt-fab__channel-name {
  font-family: var(--font-display);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-primary);
}

.tt-fab__channel-hint {
  font-size: var(--text-xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-secondary);
}

/* =========================================================
   SOCIAL BRAND COLORS — applied to channel/contact icons
   so the destination brand is recognizable at a glance.
   ========================================================= */

/* Instagram brand gradient (applied via background + clip on icon swatch) */
.tt-fab__channel--instagram .tt-fab__channel-icon,
.tt-contact-line__icon--instagram {
  background: var(--brand-instagram);
  color: var(--text-on-fill);
}

/* WhatsApp brand green */
.tt-fab__channel--whatsapp .tt-fab__channel-icon,
.tt-contact-line__icon--whatsapp {
  background: var(--brand-whatsapp);
  color: var(--text-on-fill);
}

/* Email — Gmail red */
.tt-fab__channel--email .tt-fab__channel-icon,
.tt-contact-line__icon--email {
  background: var(--brand-email);
  color: var(--text-on-fill);
}

/* Channel-card hover: borders also adopt brand for cohesion */
.tt-fab__channel--whatsapp:hover { border-color: var(--brand-whatsapp); }
.tt-fab__channel--instagram:hover { border-color: var(--brand-instagram-edge); }
.tt-fab__channel--email:hover { border-color: var(--brand-email); }

/* =========================================================
   FINAL CTA — the page's closing ask

   Deliberately not a card. Every sibling section states itself through
   .tt-section__title at --text-4xl; this one used to sit at --text-2xl inside a
   640px box, two steps down the ladder from "How it works", which made the last
   ask on the page quieter than the section explaining aftercare. It now takes
   the hero's register one step below the hero itself, so the page opens and
   closes in the same voice and the scroll ends on a peak.

   No channel was dropped. The phone, the email address, the Instagram handle and
   the service area are all still here, ranked into three tiers (action / value /
   channel) instead of listed as equals: the number is printed once with its ways
   as chips beneath it, Instagram and email are self-sized chips, and the area is
   a note. What went are the duplicate wrappers, not the values — the email ghost
   button and the row of naked social pills each restated a channel the block
   already carried.

   These rules are scoped to tt-final*, so the tt-contact-* components stay intact
   for contacts.html. The one shared piece is tt-ways, the chip row under a value;
   it lives outside both because keeping a private copy per page is how the two
   ended up at different heights.
   ========================================================= */

/* No background band: it was tried and it made the section read as a second
   footer, because the footer's first band already carries this same ask.

   Every value is here on purpose: stripping the phone number, the service area
   and Instagram out of the conversion moment costs leads, whatever it does for
   purity. Do not remove them again. */
.tt-final__inner {
  max-width: var(--max-width-narrow);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-5);
}

/* Keeps the hero's scale but not its case. Uppercase at 60px was a shout, and with
   four channel rows underneath the whole block had every dial turned up at once.
   Sentence case at the same --text-5xl leaves it the largest type below the hero —
   the peak survives — while dropping one notch of volume. Tracking moves to
   --track-title with the case, because --track-display is cut for caps. */
.tt-final__title {
  font-family: var(--font-display);
  font-size: var(--text-5xl);
  font-weight: 500;
  line-height: var(--leading-tight);
  letter-spacing: var(--track-title);
  color: var(--text-primary);
  max-width: 24ch;
}

/* The site's signature headline move, in the hero's direction: where a section
   title is 700 and its accent word drops to 500, display weight starts at 500
   so the accent word breaks *heavier*. Same device, same tokens. */
.tt-final__title em {
  font-style: normal;
  font-weight: 700;
  color: var(--accent-text);
}

.tt-final__sub {
  font-size: var(--text-lg);
  color: var(--text-secondary);
  line-height: var(--leading-body);
  max-width: 48ch;
}

/* The single action, and it closes the block. It used to sit under the subtitle,
   which gave the section two endings: the most saturated element read as the close
   while three quieter rows still followed it. Last means last.
   Twice the section's internal rhythm above it (20px gap + 20px margin against a
   20px stack) so it reads as the terminus rather than as one more line in the list —
   without that separation the move only half works. */
.tt-final__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  margin-top: var(--space-5);
}

/* Rank comes from the wider padding; the step up to --text-base was one dial too
   many when the headline was also uppercase. */
.tt-final__cta {
  padding: var(--space-4) var(--space-8);
}

.tt-final__cta svg {
  width: 20px;
  height: 20px;
  flex: none;
}

/* Everything below the statement is centred and self-sized. The earlier version
   stretched .tt-contact-line rows across a two-column grid; with their hint slot
   removed the value's `flex: 1` expanded into empty space, so every row lost its
   right-hand anchor and the full-width number row became a 832px box holding 290px
   of content. Nothing here stretches to an edge it has no content for. */

/* The number, set in the display face because it is a value worth acting on, with
   the remaining ways beneath it. Its own group so the pair stays tight while the
   section's stack keeps its --space-5 rhythm. */
.tt-final__phone {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-2);
}

.tt-final__number {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 500;
  letter-spacing: var(--track-title);
  line-height: var(--leading-none);
  color: var(--text-primary);
}

/* WAYS — the chips under a value that name the other ways to use it. Shared by the
   home page's closing CTA and the contacts card: a second private copy of this rule
   is exactly how two different chip heights happened in the first place. Tag-shaped,
   which is the one rectangle this system lets carry a pill radius.

   44px tall — 36px clears WCAG 2.5.8 but not the 44px of 2.5.5, and these are
   conversion paths. Label size in --text-secondary so an added channel adds no
   volume; a way stays subordinate to a channel chip by type size, padding, border
   weight and the absence of a glyph, which is four signals without spending height. */
.tt-ways {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.tt-ways a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--space-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--text-secondary);
  transition: border-color var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out);
}

.tt-ways a:hover {
  border-color: var(--accent);
  color: var(--accent-text);
}

/* The closing CTA centres its whole stack. The contacts card instead hangs the ways
   under the value they belong to, which starts past the row's 12px padding, its 40px
   swatch and the 16px gap between them. */
.tt-ways--centered { justify-content: center; }
.tt-ways--under-value { padding-left: calc(40px + var(--space-4) + var(--space-3)); }

/* The channels whose value is the label: a handle reads as Instagram and an address
   reads as an email, so neither needs a caption. Sized to content and centred. */
.tt-final__chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2);
}

.tt-final__chips a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: 0 var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  transition: border-color var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out);
}

.tt-final__chips svg {
  width: 18px;
  height: 18px;
  flex: none;
}

.tt-final__chips a:hover {
  border-color: var(--accent);
  color: var(--accent-text);
}

/* A service area is information, not a way to reach anyone, so it is a note rather
   than a channel. The value stays for local search and for answering "where". */
.tt-final__note {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

.tt-final__note svg {
  width: 16px;
  height: 16px;
  flex: none;
}

