/* ═══════════════════════════════════════════════════════════════════════════
   Portfolio — the public page
   ───────────────────────────────────────────────────────────────────────────
   One stylesheet for every portfolio, whatever template it started from. The
   differences between templates live in the document (its theme variables and
   the styles on each block), not here, so a client who moves everything around
   still gets a page that holds together.

   Everything is driven by the eight variables the page sets on :root. This
   file is deliberately small: it is on the critical path of a stranger's first
   visit, usually on a phone, often on mobile data.
   ═══════════════════════════════════════════════════════════════════════════ */

*, *::before, *::after { box-sizing: border-box; }

.pf-body {
  margin: 0;
  background: var(--pf-bg, #fff);
  color: var(--pf-text, #101828);
  font-family: var(--pf-font, Inter, system-ui, sans-serif);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { max-width: 100%; height: auto; display: block; }
a { color: inherit; }

/* ── the draft notice ───────────────────────────────────────────────────── */
.pf-draft {
  background: #1f2937; color: #fff;
  font-family: Inter, system-ui, sans-serif;
  font-size: 13px; text-align: center;
  padding: 10px 16px;
  position: sticky; top: 0; z-index: 20;
}

/* ── structure ──────────────────────────────────────────────────────────── */
.pf-section { position: relative; }
.pf-wrap {
  max-width: var(--pf-max, 1140px);
  margin: 0 auto;
  padding-left: 24px; padding-right: 24px;
}
.pf-row {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 32px;
  align-items: start;
}
.pf-col { min-width: 0; }
/* A row of cards should end level. Only the columns that hold one stretch,
   so an ordinary two-column row still sizes to its content. */
.pf-col:has(> .pf-pk), .pf-col:has(> .pf-service) { align-self: stretch; }
.pf-col > .pf-pk, .pf-col > .pf-service { height: 100%; }
.pf-col-1  { grid-column: span 1; }
.pf-col-2  { grid-column: span 2; }
.pf-col-3  { grid-column: span 3; }
.pf-col-4  { grid-column: span 4; }
.pf-col-5  { grid-column: span 5; }
.pf-col-6  { grid-column: span 6; }
.pf-col-7  { grid-column: span 7; }
.pf-col-8  { grid-column: span 8; }
.pf-col-9  { grid-column: span 9; }
.pf-col-10 { grid-column: span 10; }
.pf-col-11 { grid-column: span 11; }
.pf-col-12 { grid-column: span 12; }

/* ── blocks ─────────────────────────────────────────────────────────────── */
.pf-h {
  margin: 0 0 16px;
  line-height: 1.15;
  letter-spacing: -0.02em;
  font-weight: 700;
  color: inherit;
}
.pf-h:last-child { margin-bottom: 0; }

.pf-text { color: var(--pf-muted, #5f6a7d); }
.pf-text p { margin: 0 0 14px; }
.pf-text p:last-child { margin-bottom: 0; }

.pf-fig { margin: 0; }
.pf-fig img {
  width: 100%;
  border-radius: var(--pf-radius, 14px);
  object-fit: cover;
}

/* A block with no image yet, on the public page as well as in the builder:
   an empty gap reads as a broken page, a marked one reads as unfinished. */
.pf-ph {
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: var(--pf-radius, 14px);
  background: var(--pf-surface, #f6f7f9);
  color: var(--pf-muted, #5f6a7d);
  display: flex; align-items: center; justify-content: center;
  opacity: .55;
}
.pf-ph svg { width: 38px; height: 38px; }

/* Set the way a studio would set it: small, tracked and in capitals rather
   than a large web button. The size difference is what separates a
   photographer's site from an app's landing page. */
.pf-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 15px 30px;
  background: var(--pf-accent, #4f46e5);
  color: #fff;
  border-radius: var(--pf-radius, 14px);
  font-size: 13px; font-weight: 650;
  letter-spacing: .09em; text-transform: uppercase;
  text-decoration: none;
  transition: opacity .15s ease, transform .15s ease;
}
.pf-btn:hover { opacity: .9; transform: translateY(-1px); }

.pf-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 16px;
}
/* The frame is fixed and the picture moves inside it, very slightly, on
   hover. A gallery that answers the pointer reads as a portfolio; one that
   sits still reads as a contact sheet. */
.pf-gallery figure {
  margin: 0; overflow: hidden;
  border-radius: var(--pf-radius, 14px);
}
.pf-gallery img,
.pf-gallery .pf-ph {
  width: 100%; aspect-ratio: 1 / 1; object-fit: cover;
  border-radius: var(--pf-radius, 14px);
  transition: transform .6s cubic-bezier(.2, .7, .3, 1);
}
.pf-gallery figure:hover img { transform: scale(1.035); }

.pf-video {
  position: relative; width: 100%; aspect-ratio: 16 / 9;
  border-radius: var(--pf-radius, 14px); overflow: hidden;
  background: var(--pf-surface, #f6f7f9);
}
.pf-video iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

.pf-social { display: flex; flex-wrap: wrap; gap: 10px; }
.pf-social a {
  display: inline-flex; align-items: center;
  padding: 9px 16px;
  border: 1px solid currentColor;
  border-radius: 999px;
  font-size: 13.5px; font-weight: 600;
  text-decoration: none;
  color: var(--pf-muted, #5f6a7d);
  transition: color .15s ease;
}
.pf-social a:hover { color: var(--pf-accent, #4f46e5); }
/* Centred by the block's own alignment rather than by a second rule. */
[style*="text-align:center"] > .pf-social,
.pf-social[style*="text-align:center"] { justify-content: center; }

/* A block narrower than its column that centres its own text is centred as
   a whole. Without this, a 620px lede in a centred section sat hard against
   the left edge with its words centred inside it, which reads as a mistake
   rather than as a measure. */
[style*="text-align:center"][style*="max-width"] { margin-left: auto; margin-right: auto; }

.pf-contact { font-size: 20px; font-weight: 600; }
.pf-contact a { color: var(--pf-accent, #4f46e5); text-decoration: none; }
.pf-contact a:hover { text-decoration: underline; }

.pf-quote {
  margin: 0;
  padding: 30px 32px 28px;
  background: var(--pf-surface, #f6f7f9);
  border-radius: var(--pf-radius, 14px);
}
/* A quotation mark set in the page's own display face, large and faint. It
   is decoration, so it is hidden from anything reading the page aloud. */
.pf-quote::before {
  content: "\201C";
  display: block; margin-bottom: -14px;
  font-size: 56px; line-height: 1;
  color: var(--pf-accent, #4f46e5); opacity: .32;
}
/* The size is set on the quotation, and the paragraphs inside it follow.
   Setting it on the paragraph instead meant a template asking for a 24px
   pull quote got 17px, because this file is more specific than the block. */
.pf-quote { font-size: 17px; }
.pf-quote p { margin: 0 0 14px; font-size: inherit; line-height: 1.7; }
.pf-quote cite {
  font-style: normal; font-size: 13px; font-weight: 650;
  color: var(--pf-muted, #5f6a7d);
}

/* A hairline over each service turns three paragraphs into a set. Neutral,
   so it reads the same on a near-black template as on an off-white one. */
.pf-service { padding-top: 20px; border-top: 1px solid rgba(128,128,128,.28); }
.pf-service h3 {
  margin: 0 0 10px;
  font-size: 19px; font-weight: 700; letter-spacing: -0.015em;
}
.pf-service p { margin: 0; color: var(--pf-muted, #5f6a7d); font-size: 15px; line-height: 1.65; }

.pf-project {
  display: flex; align-items: baseline; justify-content: space-between; gap: 18px;
  padding: 15px 0;
  border-bottom: 1px solid currentColor;
  text-decoration: none;
  color: inherit;
}
.pf-project:last-child { border-bottom: 0; }
.pf-project-t { font-size: 16px; font-weight: 600; }
.pf-project-m { font-size: 13px; color: var(--pf-muted, #5f6a7d); white-space: nowrap; }

.pf-stat { text-align: center; }
.pf-stat-n {
  display: block;
  font-size: clamp(34px, 4vw, 52px); font-weight: 800;
  letter-spacing: -0.03em; line-height: 1; font-variant-numeric: tabular-nums;
  color: var(--pf-accent, #4f46e5);
}
.pf-stat-l {
  display: block; margin-top: 8px;
  font-size: 13.5px; color: var(--pf-muted, #5f6a7d);
}

.pf-rule { border: 0; border-top: 1px solid currentColor; opacity: .2; margin: 0; }
.pf-spacer { width: 100%; }
.pf-embed > :first-child { margin-top: 0; }
.pf-embed > :last-child { margin-bottom: 0; }

/* ── header ─────────────────────────────────────────────────────────────── */
.pf-header { background: var(--pf-bg, #fff); border-bottom: 1px solid rgba(128,128,128,.14); }
.pf-header.is-sticky { position: sticky; top: 0; z-index: 30; backdrop-filter: saturate(1.4) blur(8px); }
.pf-header-in {
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px; padding-top: 16px; padding-bottom: 16px; flex-wrap: wrap;
}
.pf-brand { display: flex; align-items: center; gap: 11px; min-width: 0; }
.pf-brand-logo { max-height: 40px; width: auto; object-fit: contain; }
/* Where a logo goes, in the builder and on the template cards (2.154.0) —
   never on a public page. In the builder it is a button to choose one. */
.pf-logo-ph {
  display: inline-flex; align-items: center; justify-content: center;
  height: 36px; padding: 0 14px; border-radius: 8px;
  border: 1.5px dashed rgba(128,128,128,.55);
  color: var(--pf-muted, #5f6a7d); font: 650 12px/1 Inter, system-ui, sans-serif;
  letter-spacing: .12em; text-transform: uppercase; white-space: nowrap;
}
button.pf-logo-ph { background: transparent; cursor: pointer; }
button.pf-logo-ph:hover { color: var(--pf-text, #101828); border-color: rgba(128,128,128,.9); }
.pf-brand-t {
  display: flex; flex-direction: column; gap: 1px;
  font-size: 17px; font-weight: 700; letter-spacing: -.015em; line-height: 1.2;
}
.pf-brand-t small { font-size: 12px; font-weight: 400; color: var(--pf-muted, #5f6a7d); letter-spacing: 0; }
.pf-nav { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.pf-nav a {
  font-size: 12.5px; font-weight: 600; text-decoration: none;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--pf-muted, #5f6a7d); transition: color .15s ease;
}
.pf-nav a:hover { color: var(--pf-accent, #4f46e5); }
/* The one button in the bar (2.153.0): set apart from the menu, in the
   page's accent, the size of the menu's own type rather than a web button. */
.pf-nav a.pf-head-cta {
  display: inline-flex; align-items: center;
  padding: 10px 18px; margin-left: 4px;
  background: var(--pf-accent, #4f46e5); color: #fff;
  border-radius: min(var(--pf-radius, 14px), 999px);
  letter-spacing: .12em;
  transition: opacity .15s ease, transform .15s ease;
}
.pf-nav a.pf-head-cta:hover { color: #fff; opacity: .9; transform: translateY(-1px); }

/* The header's #work, #pricing and #contact land below a sticky header, not
   under it. */
.pf-section, .pf-anchor { scroll-margin-top: 88px; }
/* The centred and split bars are two rows — the mark above the menu — and
   stand taller, so a jump stops lower to clear them. */
.pf-body:has(.pf-header--centred.is-sticky, .pf-header--split.is-sticky) :is(.pf-section, .pf-anchor) { scroll-margin-top: 136px; }
.pf-anchor { display: block; height: 0; }

/* ── header layouts ─────────────────────────────────────────────────────
   Four arrangements of the same two pieces. mark-left is the default and is
   what .pf-header-in already does, so it needs no rule of its own.

   "centred" is two rows on purpose. Centring the mark in a single row while
   keeping the menu beside it needs absolute positioning, which stops being
   centred the moment the menu wraps — and on a phone the menu always wraps. */
.pf-header--mark-right .pf-header-in { flex-direction: row-reverse; }

.pf-header--centred .pf-header-in {
  flex-direction: column; justify-content: center; gap: 12px;
}
.pf-header--centred .pf-brand { justify-content: center; }
.pf-header--centred .pf-nav { justify-content: center; }

/* "split" is the only one that genuinely needs three tracks, and it collapses
   to the centred stack below the width where two menus either side of a mark
   stop fitting. */
.pf-header--split .pf-header-in {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
}
.pf-header--split .pf-brand { justify-content: center; }
.pf-header--split .pf-nav-a { justify-content: flex-start; }
.pf-header--split .pf-nav-b { justify-content: flex-end; }

@media (max-width: 720px) {
  .pf-header--split .pf-header-in {
    display: flex; flex-direction: column; justify-content: center; gap: 12px;
  }
  .pf-header--split .pf-nav-a,
  .pf-header--split .pf-nav-b { justify-content: center; }
  .pf-header--mark-right .pf-header-in { flex-direction: column; }
  /* On a phone the bar wraps to two or three rows — 165px of an 844px
     screen. Held at the top it hid a fifth of every page and the heading of
     any section the menu jumped to, so on a phone it scrolls away with the
     page (2.153.0). */
  /* …and without its blur: a backdrop-filter makes the bar the box a fixed
     child is placed in, which would pin the floating button inside it. */
  .pf-header.is-sticky { position: relative; backdrop-filter: none; -webkit-backdrop-filter: none; }
  .pf-header .pf-brand { order: -1; }
  /* Two rows on a phone whatever the arrangement: the mark, then the menu in
     one line that scrolls sideways rather than wrapping into a third. */
  .pf-header .pf-header-in { display: flex; flex-direction: row; flex-wrap: wrap; gap: 10px 18px; padding-top: 14px; padding-bottom: 12px; }
  .pf-header .pf-brand { flex: 1 1 100%; min-width: 0; }
  .pf-header--mark-left .pf-brand, .pf-header--mark-right .pf-brand { justify-content: flex-start; }
  .pf-header--centred .pf-header-in, .pf-header--split .pf-header-in { justify-content: center; }
  .pf-header .pf-brand-logo { max-height: 32px; max-width: 62vw; }
  .pf-header .pf-nav {
    flex: 0 1 auto; max-width: 100%; flex-wrap: nowrap; overflow-x: auto; gap: 18px;
    scrollbar-width: none; -webkit-overflow-scrolling: touch;
  }
  .pf-header .pf-nav::-webkit-scrollbar { display: none; }
  .pf-header .pf-nav a { white-space: nowrap; }
  .pf-header--mark-left .pf-header-in, .pf-header--mark-right .pf-header-in { justify-content: flex-start; }
  /* The button floats at the foot of the screen instead — in reach of a
     thumb on every part of the page — and the page leaves room for it. */
  .pf-nav a.pf-head-cta {
    position: fixed; right: 16px; bottom: 16px; z-index: 40; margin: 0;
    padding: 13px 20px; box-shadow: 0 10px 26px rgba(0, 0, 0, .24);
  }
  .pf-body:has(.pf-head-cta) { padding-bottom: 76px; }
  .pf-body :is(.pf-section, .pf-anchor),
  .pf-body:has(.pf-header--centred.is-sticky, .pf-header--split.is-sticky) :is(.pf-section, .pf-anchor) { scroll-margin-top: 12px; }
}

/* ── gallery categories ─────────────────────────────────────────────────
   Filtering hides with a class rather than removing nodes, so the whole
   gallery is in the HTML for a search engine and for a visitor with no
   JavaScript. The buttons only appear when there are at least two
   categories — one category is a label, not a filter. */
.pf-cats {
  display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px;
}
.pf-cat {
  font: inherit; font-size: 11.5px; font-weight: 650; line-height: 1;
  letter-spacing: .09em; text-transform: uppercase;
  padding: 10px 16px; border: 0; border-radius: 99px; cursor: pointer;
  background: var(--pf-surface, #f1f3f7); color: var(--pf-muted, #5f6a7d);
  transition: background .15s ease, color .15s ease;
}
.pf-cat:hover { color: var(--pf-text, #10141c); }
.pf-cat.is-on { background: var(--pf-accent, #4f46e5); color: #fff; }
.pf-gallery figure.is-off { display: none; }


/* ── price packages ─────────────────────────────────────────────────────── */
/* A hairline rather than a filled block. Three grey panels side by side is
   a pricing table from a software site; three ruled columns is a rate card,
   which is what a studio hands to a client. */
.pf-pk {
  display: flex; flex-direction: column; height: 100%;
  background: transparent;
  border: 1px solid rgba(128,128,128,.26);
  border-radius: var(--pf-radius, 14px);
  overflow: hidden;
}
/* The one being pushed reads differently without shouting. */
.pf-pk.is-featured {
  background: var(--pf-surface, #f6f7f9);
  border-color: transparent;
  outline: 2px solid var(--pf-accent, #4f46e5); outline-offset: -2px;
}
.pf-pk-img { margin: 0; }
.pf-pk-img img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; }
.pf-pk-b { padding: 28px 26px 26px; display: flex; flex-direction: column; gap: 11px; flex: 1 1 auto; }
.pf-pk-tag {
  align-self: flex-start;
  padding: 4px 10px; border-radius: 999px;
  background: var(--pf-accent, #4f46e5); color: #fff;
  font-size: 10.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
}
.pf-pk-n { margin: 0; font-size: 19px; font-weight: 700; letter-spacing: -.015em; }
.pf-pk-p { margin: 0; display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.pf-pk-p span { font-size: clamp(26px, 3vw, 34px); font-weight: 800; letter-spacing: -.03em; line-height: 1; font-variant-numeric: tabular-nums; }
.pf-pk-p small { font-size: 12.5px; color: var(--pf-muted, #5f6a7d); }
.pf-pk-d { margin: 0; font-size: 14px; line-height: 1.6; color: var(--pf-muted, #5f6a7d); }
.pf-pk-f { list-style: none; margin: 4px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.pf-pk-f li {
  position: relative; padding-left: 21px;
  font-size: 14px; line-height: 1.55; color: var(--pf-muted, #5f6a7d);
}
.pf-pk-f li::before {
  content: ""; position: absolute; left: 2px; top: .52em;
  width: 9px; height: 5px; border-left: 2px solid var(--pf-accent, #4f46e5);
  border-bottom: 2px solid var(--pf-accent, #4f46e5); transform: rotate(-45deg);
}
.pf-pk-a { display: flex; gap: 8px; margin-top: auto; padding-top: 16px; flex-wrap: wrap; }
.pf-pk-a a {
  flex: 1 1 auto; text-align: center;
  padding: 12px 16px; border-radius: var(--pf-radius, 14px);
  font-size: 12px; font-weight: 650; letter-spacing: .08em; text-transform: uppercase;
  text-decoration: none;
  transition: opacity .15s ease;
}
.pf-pk-a a:hover { opacity: .88; }
.pf-pk-wa { background: #075e54; color: #fff; }
.pf-pk-tel { background: var(--pf-accent, #4f46e5); color: #fff; }

/* ── footer ─────────────────────────────────────────────────────────────── */
.pf-footer {
  background: var(--pf-surface, #f6f7f9);
  border-top: 1px solid rgba(128,128,128,.14);
  padding-top: 44px; padding-bottom: 34px;
  font-family: Inter, system-ui, sans-serif;
  color: var(--pf-muted, #5f6a7d);
}
.pf-footer-top {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 28px; flex-wrap: wrap;
  padding-bottom: 28px; margin-bottom: 22px;
  border-bottom: 1px solid rgba(128,128,128,.14);
}
.pf-footer-who { display: flex; flex-direction: column; gap: 7px; max-width: 320px; font-size: 13px; line-height: 1.6; }
.pf-footer-who strong { font-size: 15px; color: var(--pf-text, #101828); }
.pf-footer-links { display: flex; flex-direction: column; gap: 8px; }
.pf-footer-links a { font-size: 13px; text-decoration: none; color: var(--pf-muted, #5f6a7d); }
.pf-footer-links a:hover { color: var(--pf-accent, #4f46e5); }
/* The end of every portfolio, always in this order and always centred. */
.pf-footer-end {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  text-align: center; font-size: 12.5px;
}
.pf-copy { color: var(--pf-muted, #5f6a7d); }
/* Our line on every client portfolio. Quiet on purpose — smaller than the
   copyright above it — but quiet by size, not by fading it out of
   legibility. It follows the studio's own muted colour, so it sits inside
   their palette rather than fighting it. */
.pf-by { font-size: 11.5px; color: var(--pf-muted, #5f6a7d); }


/* ── narrower ───────────────────────────────────────────────────────────────
   Columns collapse in two steps rather than one: a 4-across row becomes two
   across on a tablet before it becomes one on a phone, which keeps a services
   row from turning into a very long list. */
@media (max-width: 900px) {
  .pf-row { gap: 26px; }
  .pf-col-1, .pf-col-2, .pf-col-3, .pf-col-4 { grid-column: span 6; }
  .pf-col-5, .pf-col-7, .pf-col-8 { grid-column: span 12; }
}
@media (max-width: 680px) {
  .pf-wrap { padding-left: 18px; padding-right: 18px; }
  .pf-row { gap: 22px; }
  .pf-col { grid-column: span 12 !important; }
  /* A template sets generous padding for a desktop — up to 150px — and on a
     phone that is most of a screen of nothing. Capped, not flattened: a
     section that declared no padding is left alone because the variable it
     would be capped through was never set. */
  .pf-section[style*="--pf-pt"] { padding-top: min(var(--pf-pt), 64px) !important; }
  .pf-section[style*="--pf-pb"] { padding-bottom: min(var(--pf-pb), 64px) !important; }
  .pf-btn { width: 100%; }
  .pf-gallery { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
  /* A spacer exists to push a column down beside another one. With the
     columns stacked there is nothing to push past, so it is capped. */
  .pf-spacer { max-height: 32px; }
  .pf-contact { font-size: 17px; word-break: break-word; }
  .pf-project { flex-direction: column; gap: 4px; }
  .pf-project-m { white-space: normal; }
  .pf-footer { justify-content: center; text-align: center; padding-bottom: 34px; }
}
@media (max-width: 400px) {
  .pf-wrap { padding-left: 14px; padding-right: 14px; }
  .pf-quote { padding: 20px 18px; }
}

@media (prefers-reduced-motion: reduce) {
  .pf-btn { transition: none; }
  .pf-btn:hover { transform: none; }
}

/* ── motion (2.153.0) ───────────────────────────────────────────────────────
   Each template's sections arrive their own way as they are scrolled to.
   The page adds .pf-anim to <html> only once its script is running and only
   on a screen whose reader has not asked for less motion, and the script
   marks each section .is-in as it comes into view — so without the script,
   in print, or with reduced motion asked for, everything is simply there.
   Nothing here ever makes content visible that would otherwise be hidden;
   it only holds back, for a moment, what is about to be shown. */
@media screen and (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }

  .pf-anim .pf-section .pf-col > *,
  .pf-anim .pf-section .pf-gallery figure {
    transition-property: opacity, transform, filter, clip-path;
    transition-duration: .9s;
    transition-timing-function: cubic-bezier(.2, .7, .2, 1);
  }
  /* Columns follow one another, so a row reads left to right. */
  .pf-anim .pf-section .pf-col:nth-child(2) > * { transition-delay: .12s; }
  .pf-anim .pf-section .pf-col:nth-child(3) > * { transition-delay: .24s; }
  .pf-anim .pf-section .pf-col:nth-child(4) > * { transition-delay: .36s; }

  /* Atelier — rises, a column at a time */
  .pf-anim .pf-motion-rise .pf-section:not(.is-in) .pf-col > * { opacity: 0; transform: translateY(34px); }

  /* Maison — a slow veil, headings settling from wide tracking */
  .pf-anim .pf-motion-veil .pf-section .pf-col > * { transition-duration: 1.6s; }
  .pf-anim .pf-motion-veil .pf-section:not(.is-in) .pf-col > * { opacity: 0; }
  .pf-anim .pf-motion-veil .pf-section .pf-h { transition-property: opacity, letter-spacing; transition-duration: 1.8s; }
  .pf-anim .pf-motion-veil .pf-section:not(.is-in) .pf-h { letter-spacing: .22em; }

  /* Monograph — pictures wiped in from the left, type close behind */
  .pf-anim .pf-motion-wipe .pf-section:not(.is-in) .pf-fig { clip-path: inset(0 100% 0 0); }
  .pf-anim .pf-motion-wipe .pf-section .pf-fig { transition-duration: 1.1s; transition-timing-function: cubic-bezier(.7, 0, .2, 1); }
  .pf-anim .pf-motion-wipe .pf-section:not(.is-in) .pf-col > :not(.pf-fig) { opacity: 0; transform: translateX(-14px); }

  /* Aurelia — opens like a flower: soft scale and fade */
  .pf-anim .pf-motion-bloom .pf-section:not(.is-in) .pf-col > *,
  .pf-anim .pf-motion-bloom .pf-section:not(.is-in) .pf-gallery figure { opacity: 0; transform: scale(.94); }
  .pf-anim .pf-motion-bloom .pf-section .pf-gallery figure:nth-child(2n) { transition-delay: .1s; }
  .pf-anim .pf-motion-bloom .pf-section .pf-gallery figure:nth-child(3n) { transition-delay: .2s; }

  /* Meridian — slides in from the left, exact and quick */
  .pf-anim .pf-motion-slide .pf-section .pf-col > * { transition-duration: .7s; }
  .pf-anim .pf-motion-slide .pf-section:not(.is-in) .pf-col > * { opacity: 0; transform: translateX(-44px); }

  /* Obsidian — out of focus and dark, then the frame clears */
  .pf-anim .pf-motion-cinema .pf-section .pf-col > *,
  .pf-anim .pf-motion-cinema .pf-section .pf-gallery figure { transition-duration: 1.4s; }
  .pf-anim .pf-motion-cinema .pf-section:not(.is-in) .pf-col > *,
  .pf-anim .pf-motion-cinema .pf-section:not(.is-in) .pf-gallery figure { opacity: 0; filter: blur(10px) brightness(.55); transform: scale(1.03); }

  /* Broadsheet — printed down the page, top to bottom */
  .pf-anim .pf-motion-press .pf-section .pf-col > * { transition-duration: 1s; transition-timing-function: cubic-bezier(.6, 0, .2, 1); }
  .pf-anim .pf-motion-press .pf-section:not(.is-in) .pf-col > * { clip-path: inset(0 0 100% 0); opacity: .2; }

  /* Lightbox — the pictures arrive as tiles, one after another */
  .pf-anim .pf-motion-tiles .pf-section:not(.is-in) .pf-gallery figure { opacity: 0; transform: translateY(18px) scale(.92); }
  .pf-anim .pf-motion-tiles .pf-section:not(.is-in) .pf-col > :not(.pf-gallery):not(.pf-galleryset) { opacity: 0; }
  .pf-anim .pf-motion-tiles .pf-section .pf-gallery figure:nth-child(1) { transition-delay: 0s; }
  .pf-anim .pf-motion-tiles .pf-section .pf-gallery figure:nth-child(2) { transition-delay: .07s; }
  .pf-anim .pf-motion-tiles .pf-section .pf-gallery figure:nth-child(3) { transition-delay: .14s; }
  .pf-anim .pf-motion-tiles .pf-section .pf-gallery figure:nth-child(4) { transition-delay: .21s; }
  .pf-anim .pf-motion-tiles .pf-section .pf-gallery figure:nth-child(5) { transition-delay: .28s; }
  .pf-anim .pf-motion-tiles .pf-section .pf-gallery figure:nth-child(6) { transition-delay: .35s; }
  .pf-anim .pf-motion-tiles .pf-section .pf-gallery figure:nth-child(7) { transition-delay: .42s; }
  .pf-anim .pf-motion-tiles .pf-section .pf-gallery figure:nth-child(8) { transition-delay: .49s; }
  .pf-anim .pf-motion-tiles .pf-section .pf-gallery figure:nth-child(n+9) { transition-delay: .56s; }

  /* Salon — rows drift in from alternate sides, like pages turned */
  .pf-anim .pf-motion-drift .pf-section .pf-col > * { transition-duration: 1.2s; }
  .pf-anim .pf-motion-drift .pf-section:nth-of-type(odd):not(.is-in) .pf-col > * { opacity: 0; transform: translateX(-36px); }
  .pf-anim .pf-motion-drift .pf-section:nth-of-type(even):not(.is-in) .pf-col > * { opacity: 0; transform: translateX(36px); }

  /* Quarto — type is set line by line from below; the plate follows */
  .pf-anim .pf-motion-type .pf-section .pf-h,
  .pf-anim .pf-motion-type .pf-section .pf-text { transition-duration: .8s; }
  .pf-anim .pf-motion-type .pf-section:not(.is-in) .pf-h,
  .pf-anim .pf-motion-type .pf-section:not(.is-in) .pf-text { clip-path: inset(100% 0 0 0); transform: translateY(12px); }
  .pf-anim .pf-motion-type .pf-section:not(.is-in) .pf-col > :not(.pf-h):not(.pf-text) { opacity: 0; }

  /* Atrium — floats up, slowly, with a lot of air */
  .pf-anim .pf-motion-float .pf-section .pf-col > * { transition-duration: 1.7s; transition-timing-function: cubic-bezier(.16, 1, .3, 1); }
  .pf-anim .pf-motion-float .pf-section:not(.is-in) .pf-col > * { opacity: 0; transform: translateY(64px); }

  /* Panorama — the plates settle from slightly wide, the way the eye
     takes in a view */
  .pf-anim .pf-motion-pan .pf-section .pf-fig { overflow: hidden; border-radius: var(--pf-radius, 14px); }
  .pf-anim .pf-motion-pan .pf-section .pf-fig img { transition: transform 2.4s cubic-bezier(.16, 1, .3, 1), opacity 1.2s ease; }
  .pf-anim .pf-motion-pan .pf-section:not(.is-in) .pf-fig img { transform: scale(1.14); opacity: 0; }
  .pf-anim .pf-motion-pan .pf-section:not(.is-in) .pf-col > :not(.pf-fig) { opacity: 0; transform: translateY(20px); }

  /* Once a section has arrived its blocks answer the pointer at their own
     speed again — a button's hover is not a two-second fade. */
  .pf-anim .pf-section.is-settled .pf-col > *,
  .pf-anim .pf-section.is-settled .pf-gallery figure { transition: none; }
  .pf-anim .pf-section.is-settled .pf-btn { transition: opacity .15s ease, transform .15s ease; }
}

/* A printed portfolio is a portfolio someone liked enough to print. */
@media print {
  .pf-draft { display: none; }
  .pf-body { background: #fff; color: #000; }
  .pf-section { break-inside: avoid; }
}

/* ════════════════════════════════════════════════════════════════════════
   THE INSTRUCTION GUIDE

   Five steps that animate the action they describe. Drawn rather than
   recorded: a screen capture would be an order of magnitude heavier, would
   be wrong the first time a button moved, and could not be read aloud.

   Only the step on screen animates. The others are display:none, so nothing
   is being composited behind the panel, and each step restarts from its
   first frame every time it is opened.
   ════════════════════════════════════════════════════════════════════════ */

.pfg { position: fixed; inset: 0; z-index: 1200; display: grid; place-items: center; padding: 20px; }
.pfg[hidden] { display: none; }
.pfg-back { position: absolute; inset: 0; background: rgba(8, 10, 16, .62); backdrop-filter: blur(3px); }

.pfg-panel {
  position: relative;
  width: min(760px, 100%);
  max-height: min(92vh, 720px);
  display: flex;
  flex-direction: column;
  background: var(--card, #fff);
  color: var(--text, #111);
  border: 1px solid var(--border, #e5e7eb);
  border-radius: 16px;
  box-shadow: 0 24px 80px rgba(8, 10, 16, .35);
  overflow: hidden;
}
.pfg-panel:focus { outline: none; }

.pfg-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; padding: 20px 22px 14px; }
.pfg-eyebrow { font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--muted, #6b7280); }
.pfg-head h3 { margin: 4px 0 0; font-size: 19px; }
.pfg-x {
  flex-shrink: 0; width: 32px; height: 32px; display: grid; place-items: center;
  background: transparent; border: 1px solid var(--border, #e5e7eb); border-radius: 9px;
  color: inherit; cursor: pointer;
}
.pfg-x:hover { background: var(--hover, rgba(0, 0, 0, .04)); }

.pfg-rail { height: 3px; background: var(--border, #e5e7eb); }
.pfg-rail-fill { height: 100%; width: 20%; background: var(--primary, #4f46e5); transition: width .3s ease; }

.pfg-body { padding: 22px; overflow-y: auto; }

.pfg-step { display: none; }
.pfg-step.is-on { display: grid; gap: 20px; }

.pfg-art { background: var(--bg, #f7f8fa); border: 1px solid var(--border, #e5e7eb); border-radius: 12px; padding: 12px; }
.pfg-svg { display: block; width: 100%; height: auto; }
.pfg-text h4 { margin: 0 0 8px; font-size: 16px; }
.pfg-text p { margin: 0; font-size: 14px; line-height: 1.65; color: var(--muted, #6b7280); }

.pfg-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 14px 22px; border-top: 1px solid var(--border, #e5e7eb);
}
.pfg-dots { display: flex; gap: 7px; }
.pfg-dot {
  width: 8px; height: 8px; padding: 0; border: 0; border-radius: 50%;
  background: var(--border-strong, #cbd5e1); cursor: pointer;
}
.pfg-dot.is-on { background: var(--primary, #4f46e5); }
.pfg-nav { display: flex; gap: 8px; }

@media (min-width: 720px) {
  .pfg-step.is-on { grid-template-columns: 1.15fr 1fr; align-items: center; }
}

/* ── the drawings ─────────────────────────────────────────────────────── */

.pfg-svg .g-card, .pfg-svg .g-frame { fill: var(--card, #fff); stroke: var(--border, #e5e7eb); stroke-width: 1.5; }
.pfg-svg .g-line { fill: var(--border, #e5e7eb); }
.pfg-svg .g-bar-in { fill: var(--bg, #f7f8fa); stroke: var(--border, #e5e7eb); stroke-width: 1.5; }
.pfg-svg text { font-family: inherit; font-size: 9px; font-weight: 600; text-anchor: middle; }

/* 1 · choosing a design */
.pfg-svg .g-pick { stroke: var(--primary, #4f46e5); stroke-width: 2; }
.is-on .g-pick { animation: g-pick 3.2s ease-in-out infinite; }
@keyframes g-pick { 0%, 30% { stroke: var(--border, #e5e7eb); stroke-width: 1.5; } 45%, 100% { stroke: var(--primary, #4f46e5); stroke-width: 2; } }

.pfg-svg .g-tick circle { fill: var(--primary, #4f46e5); }
.pfg-svg .g-tick path { fill: none; stroke: #fff; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.is-on .g-tick { transform-origin: 168px 34px; animation: g-pop 3.2s ease-in-out infinite; }
@keyframes g-pop { 0%, 34% { transform: scale(0); } 50%, 100% { transform: scale(1); } }

.pfg-svg .g-cursor { fill: var(--text, #111); }
.is-on .g-cursor { animation: g-move 3.2s ease-in-out infinite; }
@keyframes g-move {
  0% { transform: translate(210px, 110px); }
  30%, 100% { transform: translate(140px, 74px); }
}

/* 2 · editing */
.pfg-svg .g-drop { fill: none; stroke: var(--primary, #4f46e5); stroke-width: 1.5; stroke-dasharray: 5 4; }
.is-on .g-drop { animation: g-fade 3.6s ease-in-out infinite; }
@keyframes g-fade { 0%, 55% { opacity: 1; } 70%, 100% { opacity: 0; } }

.pfg-svg .g-drag rect { fill: var(--primary, #4f46e5); }
.pfg-svg .g-drag .g-drag-l { fill: #fff; opacity: .75; }
.is-on .g-drag { animation: g-drop 3.6s ease-in-out infinite; }
@keyframes g-drop {
  0% { transform: translate(180px, 108px); }
  55%, 100% { transform: translate(105px, 52px); }
}

.pfg-svg .g-type { fill: var(--text, #111); opacity: .75; }
.is-on .g-type { animation: g-type 3.6s steps(14) infinite; }
@keyframes g-type { 0%, 62% { width: 0; } 92%, 100% { width: 150px; } }
.is-on .g-caret { animation: g-caret 3.6s infinite; }
@keyframes g-caret {
  0%, 60% { opacity: 0; transform: translateX(0); }
  62% { opacity: 1; }
  92%, 100% { opacity: 1; transform: translateX(150px); }
}

/* 3 · pictures */
.pfg-svg .g-photo rect { fill: var(--border, #e5e7eb); }
.is-on .g-photo { animation: g-in .5s ease-out both; animation-delay: calc(var(--i) * .22s); }
@keyframes g-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.pfg-svg .g-bar-bg { fill: var(--border, #e5e7eb); }
.pfg-svg .g-bar { fill: var(--primary, #4f46e5); }
.is-on .g-bar { animation: g-fill 3s ease-in-out infinite; }
@keyframes g-fill { 0% { width: 0; } 70%, 100% { width: 68px; } }

/* 4 · the address — the studio name is the FIRST thing in it */
.pfg-svg .g-lock rect { fill: #16a34a; }
.pfg-svg .g-lock path { fill: none; stroke: #16a34a; stroke-width: 1.6; }
.pfg-svg .g-url { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10px; text-anchor: start; }
.pfg-svg .g-url-name { fill: var(--primary, #4f46e5); font-weight: 700; }
.pfg-svg .g-url-rest { fill: var(--muted, #6b7280); }
.pfg-svg .g-underline { fill: var(--primary, #4f46e5); }
.is-on .g-underline { transform-origin: 60px 84px; animation: g-underline 3.4s ease-in-out infinite; }
@keyframes g-underline { 0%, 20% { transform: scaleX(0); } 45%, 100% { transform: scaleX(1); } }
.is-on .g-url-name { animation: g-glow 3.4s ease-in-out infinite; }
@keyframes g-glow { 0%, 20% { opacity: .35; } 45%, 100% { opacity: 1; } }

/* 5 · sharing the link
   The badge used to swap from Draft to Live, because a page had to be
   published before it had a link. It is live when the account is made, so
   there is no swap to animate and nothing here is ever in a draft state. */
.pfg-svg .g-badge-live rect { fill: #16a34a; }
.pfg-svg .g-badge text { fill: #fff; }

.pfg-svg .g-copy rect { fill: var(--primary, #4f46e5); }
.pfg-svg .g-copy text { fill: #fff; }
.is-on .g-copy { transform-origin: 230px 79px; animation: g-press 4s ease-in-out infinite; }
@keyframes g-press { 0%, 55% { transform: scale(1); } 62% { transform: scale(.92); } 70%, 100% { transform: scale(1); } }

.pfg-svg .g-copied rect { fill: var(--text, #111); }
.pfg-svg .g-copied text { fill: #fff; }
.pfg-svg .g-copied { opacity: 0; }
.is-on .g-copied { animation: g-copied 4s ease-in-out infinite; }
@keyframes g-copied { 0%, 60% { opacity: 0; } 70%, 90% { opacity: 1; } 100% { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  .pfg-svg * { animation: none !important; }
  /* The state each animation ENDS on is the one that explains the step, so a
     still has to show the finished state, never the starting one. */
  .pfg-svg .g-copied { opacity: 1; }
  .pfg-svg .g-type { width: 150px; }
}
