/* Veesi refinement layer (v12): page transitions, touch targets, edge finishing,
   line breaking. Loaded last; no copy, section order or scene controller changes. */

/* ---------- Page-to-page transitions ----------
   Moving between Veesi pages cross-fades with a short settle while the header
   stays in place, instead of a blank reload. Browsers without cross-document
   view transitions navigate exactly as before. */
/* The opt-in itself is inline at the top of each page's head: Chrome decides
   early, and an opt-in that arrives with a later stylesheet is sometimes missed. */
.site-header { view-transition-name: veesi-header; }
::view-transition-group(veesi-header) { animation-duration: 0s; }
::view-transition-old(veesi-header),
::view-transition-new(veesi-header) { animation-duration: .2s; }
/* The old page clears quickly before the new one settles in, so the two never
   read as a double exposure. */
::view-transition-old(root) { animation: veesi-page-out .2s cubic-bezier(0, 0, .2, 1) both; }
::view-transition-new(root) { animation: veesi-page-in .4s cubic-bezier(.2, .7, .2, 1) .12s both; }
@keyframes veesi-page-out { to { opacity: 0; } }
@keyframes veesi-page-in { from { opacity: 0; transform: translate3d(0, 12px, 0); } }

/* ---------- Touch targets ----------
   Small text links keep their look but gain a comfortable invisible hit area. */
.site-footer nav a,
.text-cta,
.faq-read-all,
.back-link,
.veesi-ai .vai-link { position: relative; }
/* The footer clips the enlarged areas so they never extend the page. */
.site-footer { overflow: clip; }
.site-footer nav a::before,
.text-cta::before,
.faq-read-all::before,
.back-link::before,
.veesi-ai .vai-link::before {
  content: "";
  position: absolute;
  inset: -12px -8px;
}

/* ---------- Edge finishing ----------
   Interior hero artwork (the Work sphere's shadow, the Services ribbon tail)
   used to end in a hard horizontal cut where the hero clips. It now fades into
   the page. The overlay paints above the artwork and below the text. */
.page-hero::after {
  content: "";
  position: absolute;
  inset: auto 0 0;
  height: clamp(44px, 6vw, 96px);
  background: linear-gradient(to bottom, rgba(251, 248, 243, 0), var(--ivory, #fbf8f3) 92%);
  z-index: -1;
  pointer-events: none;
}

/* ---------- Line breaking ----------
   No single-word last lines in running text; interior headings balance their
   lines. Headings that already set their breaks by hand are unaffected. */
main p, footer p, .faq-list summary { text-wrap: pretty; }
/* A sentence followed by its link keeps the link on a line of its own. */
.plan-details, .studio-process-note, .form-privacy { text-wrap: wrap; }
.interior-page main h1,
.interior-page main h2 { text-wrap: balance; }

/* ---------- Work tiles ----------
   The arrow answers hover and keyboard focus like every other call to action. */
.work-tile .round-arrow { transition: transform .5s cubic-bezier(.2, .7, .2, 1); }
@media (hover: hover) and (pointer: fine) {
  .work-tile:hover .round-arrow { transform: translateX(5px); }
}
.work-tile:focus-visible .round-arrow { transform: translateX(5px); }
.work-tile:focus-visible img { transform: scale(1.035); }
@media (prefers-reduced-motion: reduce) {
  .work-tile .round-arrow { transition: none; }
}

/* ---------- Entrances and focus ----------
   Keyboard focus never waits for an entrance to finish. */
.reveal-ready [data-veesi-reveal]:focus-within { transition-delay: 0s; transition-duration: .15s; }

/* ---------- Contact page ----------
   The homepage's contact section on a page of its own: no divider under the
   header, and the form is there at once instead of waiting for an entrance. */
.contact-page .contact { border-top: 0; margin-top: clamp(4px, 1.5vw, 24px); }
.contact-page .contact [data-veesi-reveal] { opacity: 1 !important; transform: none !important; transition: none !important; }

/* ---------- Work cases ----------
   Each concept carries its brief, design direction and three-page structure,
   in the same quiet label/text rhythm as the Pricing details. */
.work-case { display: grid; gap: 18px; align-content: start; min-width: 0; }
.work-case-feature { grid-column: 1 / -1; }
.case-notes { display: grid; margin: 0; }
.case-notes > div { display: grid; grid-template-columns: minmax(104px, 28%) 1fr; gap: 16px; padding: 12px 0; border-top: 1px solid var(--v2-line); }
.case-notes dt { font-size: 14px; font-weight: 500; color: #161820; }
.case-notes dd { margin: 0; font-size: 15px; line-height: 1.55; color: var(--v2-muted); }
.work-case-feature .case-notes { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0 28px; }
.work-case-feature .case-notes > div { display: block; padding-top: 16px; }
.work-case-feature .case-notes dt { margin-bottom: 8px; }
@media (max-width: 680px) {
  .work-case-feature .case-notes { grid-template-columns: 1fr; }
  .work-case-feature .case-notes > div { display: grid; grid-template-columns: 104px 1fr; gap: 14px; padding: 12px 0; }
  .work-case-feature .case-notes dt { margin: 0; }
}

/* Vero's cover is a portrait-led photograph; keep the face clear of the crop. */
.concept-card[data-concept="vero"] .concept-visual img,
#vero .work-tile img { object-position: 50% 25%; }

/* ---------- Direct contact ----------
   Email and phone sit under the contact introduction, in the same quiet
   label/value rows as the Work notes; both are ordinary links. */
.contact-direct { display: grid; margin: clamp(24px, 2.6vw, 40px) 0 0; max-width: 460px; }
.contact-direct > div { display: grid; grid-template-columns: 76px minmax(0, 1fr); gap: 14px; align-items: baseline; padding: 13px 0; border-top: 1px solid var(--v2-line); }
.contact-direct > div:last-child { border-bottom: 1px solid var(--v2-line); }
.contact-direct dt { font-size: 14px; font-weight: 500; color: #161820; }
.contact-direct dd { margin: 0; min-width: 0; font-size: clamp(15px, 1.15vw, 18px); line-height: 1.4; }
.contact-direct a { position: relative; color: #161820; text-decoration: underline; text-decoration-color: #16182040; text-underline-offset: 5px; transition: text-decoration-color .3s ease; }
.contact-direct a::before { content: ""; position: absolute; inset: -12px -8px; }
.contact-direct a:hover, .contact-direct a:focus-visible { text-decoration-color: currentColor; }
@media (max-width: 960px) { .contact-direct { margin: -8px 0 30px; } }
@media (max-width: 360px) { .contact-direct > div { grid-template-columns: 56px minmax(0, 1fr); gap: 10px; } .contact-direct dd { font-size: 14px; } }
[data-locale=zh] .contact-direct { font-family: var(--zh-sans, inherit); }
[data-locale=zh] .contact-direct a { font-family: var(--veesi-sans); }

/* ---------- Phone and tablet A$99 pill ----------
   The header's A$99 button lives in the menu below 1000px; this copy keeps the
   offer in reach while scrolling (behaviour in refine.js). It sits bottom-left,
   level with the back-to-top button on the right. */
.sticky-cta { display: none; }
@media (max-width: 1000px) {
  .sticky-cta { display: inline-flex; position: fixed; z-index: 89; left: max(16px, env(safe-area-inset-left)); bottom: calc(18px + env(safe-area-inset-bottom)); margin: 0; min-height: 48px; padding: 12px 22px; gap: 14px; font-size: 14px; box-shadow: 0 12px 26px -10px #1d2a1452, 0 2px 6px #1d2a141a; transform: translate3d(0, calc(100% + 44px), 0); visibility: hidden; transition: transform .45s cubic-bezier(.4, 0, .6, 1), visibility 0s linear .45s; }
  .sticky-cta.is-shown { transform: none; visibility: visible; transition: transform .55s cubic-bezier(.2, .7, .2, 1), visibility 0s; }
}
@media (prefers-reduced-motion: reduce) { .sticky-cta, .sticky-cta.is-shown { transition: none; } }
@media print { .sticky-cta { display: none !important; } }

/* ---------- Five-day process ----------
   Wider screens: the illustration is revealed left to right by a soft mask
   that follows --draw (0–1, set while scrolling); each day's label turns blue
   and its rule fills as the drawing reaches it. Phones: each day's picture
   arrives with its row. Without .process-motion everything is as before. */
/* The artwork's white ground used to disappear only by blending (multiply)
   with the page behind it. WebKit can drop that blend once the picture or a
   parent gets its own compositing layer, leaving a white panel on the ivory
   page, so the blend now happens inside the box: ivory ground x artwork,
   the same result in every engine, with no reliance on what lies behind. */
.process-visual, .day-visual { background-color: var(--ivory, #fbf8f3); background-blend-mode: multiply; mix-blend-mode: normal; }
/* The undrawn part is covered by an ivory veil with a soft edge that slides
   right on the compositor, so the artwork itself never repaints. */
.process-motion .process-visual { position: relative; overflow: hidden; overflow: clip; }
.process-motion .process-visual::after {
  content: ""; position: absolute; top: -1px; bottom: -1px; left: 0; width: 200%;
  background: linear-gradient(90deg, #fbf8f300 0, var(--ivory, #fbf8f3) 8%);
  transform: translate3d(calc(-8% + var(--draw, 0) * 58%), 0, 0);
  pointer-events: none;
}
.process-motion .process-visual.is-drawing::after { will-change: transform; }
.timeline h3 { position: relative; }
.timeline h3::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px; background: var(--v2-blue); transform: scaleX(0); transform-origin: 0 50%; pointer-events: none; }
.process-motion .timeline h3::after { transition: transform .8s cubic-bezier(.2, .7, .2, 1); }
.process-motion .timeline .day-label { transition: color .45s ease; }
.process-motion .timeline > li.is-reached .day-label { color: var(--v2-blue); }
.process-motion .timeline > li.is-reached h3::after { transform: none; }
@media (max-width: 680px) {
  .timeline h3::after { display: none; }
  .process-motion .timeline .day-visual { opacity: 0; transform: translate3d(0, 18px, 0) scale(.96); transition: opacity .7s ease, transform .9s cubic-bezier(.2, .7, .2, 1); }
  .process-motion .timeline > li.is-reached .day-visual { opacity: 1; transform: none; }
}

/* ---------- Plan cards ----------
   As the A$99 card lands in its entrance, its blue edge lights up. */
.reveal-ready .plan-monthly[data-veesi-reveal] {
  border-color: var(--v2-line);
  transition: opacity .9s cubic-bezier(.2, .65, .25, 1), transform 1s cubic-bezier(.2, .65, .25, 1), border-color .6s ease;
  transition-delay: calc(var(--reveal-i, 0) * 75ms), calc(var(--reveal-i, 0) * 75ms), calc(var(--reveal-i, 0) * 75ms + .3s);
}
.reveal-ready .plan-monthly[data-veesi-reveal].is-in { border-color: var(--v2-blue); }
.reveal-ready .plan-monthly[data-veesi-reveal]:focus-within { opacity: 1; transform: none; border-color: var(--v2-blue); transition-delay: 0s; transition-duration: .15s; }

/* ---------- 12-month comparison bars (Pricing page) ---------- */
.cost-comparison p::after { content: ""; display: block; width: calc(var(--share, 1) * 100%); height: 6px; margin-top: 16px; border-radius: 99px; background: #1618202e; transform-origin: 0 50%; }
.cost-comparison p:first-child::after { background: var(--v2-blue); }
.bars-armed p::after { transform: scaleX(0); transition: transform 1.1s cubic-bezier(.2, .7, .2, 1) .25s; }
.bars-armed p + p::after { transition-delay: .4s; }
.bars-armed.bars-in p::after { transform: none; }

/* ---------- Interior hero objects ----------
   Work: the glass ball rolls in from the left and settles; its blue band and
   seam turn with the travel while the lighting stays put (the approved
   Investment roll). Pricing: the ribbon sweeps in, then drifts slightly with
   the first screen's scroll where scroll timelines are supported. */
.page-sphere [data-workball-ball], .page-sphere [data-workball-contact], .page-sphere [data-workball-shadow],
.page-sphere [data-workball-band], .page-sphere [data-workball-seam] { transform-box: view-box; }
.page-sphere [data-workball-band], .page-sphere [data-workball-seam] { transform-origin: 330px 430px; }
@media (prefers-reduced-motion: no-preference) {
  .page-sphere [data-workball-ball] { animation: workball-roll 1.9s cubic-bezier(.25, .8, .25, 1) .35s both; }
  .page-sphere [data-workball-contact], .page-sphere [data-workball-shadow] { animation: workball-shadow 1.9s cubic-bezier(.25, .8, .25, 1) .35s both; }
  .page-sphere [data-workball-band], .page-sphere [data-workball-seam] { animation: workball-turn 1.9s cubic-bezier(.25, .8, .25, 1) .35s both; }
  .services-hero .page-ribbon { animation: ribbon-sweep 1.6s cubic-bezier(.16, .84, .3, 1) .1s both; }
  @supports (animation-timeline: scroll()) {
    .services-hero .page-ribbon {
      animation: ribbon-sweep 1.6s cubic-bezier(.16, .84, .3, 1) .1s both, ribbon-drift linear both;
      animation-timeline: auto, scroll(root);
      animation-range: normal, 0 80vh;
    }
  }
}
@keyframes workball-roll { from { transform: translateX(-220px); opacity: 0; } 30% { opacity: 1; } }
@keyframes workball-shadow { from { transform: translateX(-220px); opacity: 0; } }
@keyframes workball-turn { from { transform: rotate(-76deg); } }
@keyframes ribbon-sweep { from { opacity: 0; translate: 6% -7%; rotate: -9deg; } }
@keyframes ribbon-drift { to { transform: rotate(-17deg) translate3d(0, -3%, 0); } }
.motion-off .page-sphere *, .motion-off .services-hero .page-ribbon { animation: none !important; }

@media print {
  .process-motion .process-visual::after, .timeline h3::after { display: none; }
  .process-motion .timeline > li.is-reached .day-label { color: var(--v2-muted); }
  .process-motion .timeline .day-visual { opacity: 1 !important; transform: none !important; }
  .plan-monthly { border-color: var(--v2-blue) !important; }
  .bars-armed p::after { transform: none !important; }
}
