:focus-visible{outline:2px solid var(--accent);outline-offset:5px}
.sg-project__inner{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,1fr);gap:clamp(28px,6vw,100px);align-items:center}
.sg-project:nth-of-type(even) .sg-project__image{order:2}
.sg-project__copy h2,.sg-showcase h2{font-family:var(--f-display,Georgia,serif);font-weight:400;font-size:clamp(30px,3.6vw,56px);line-height:1.13;letter-spacing:-.035em;margin:16px 0 22px}
.sg-project__copy p,.sg-showcase p{max-width:58ch;line-height:1.8;color:var(--ink-soft)}
.sg-project__lede{font-size:clamp(17px,1.5vw,21px)}
.sg-project__copy dl{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin:32px 0 0;border-top:1px solid var(--rule);padding-top:24px}
.sg-project__copy dt{font-family:var(--f-mono,monospace);font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-mute)}
.sg-project__copy dd{margin:6px 0 0;font-size:14px}
.sg-project--featured{background:var(--marble)}
.sg-showcase{background:var(--marble)}
.sg-showcase__inner{display:grid;grid-template-columns:1fr 1fr;gap:clamp(25px,6vw,90px);align-items:center}
.sg-showcase img{width:100%;height:auto;max-height:460px;object-fit:contain}
.seo-hero__image-frame:has([data-sg-media]),.journal-detail__media:has([data-sg-media]){padding:0!important;height:auto!important;aspect-ratio:auto!important;overflow:visible!important;background:transparent}
.post__cover:has([data-sg-media]){height:auto!important;aspect-ratio:auto!important;padding:0!important}
@media(min-width:901px){.sg-project__image{max-width:620px}.sg-project__image:has([data-aspect="9:16"]),.sg-project__image:has([data-aspect="2:3"]){width:80%;justify-self:center}}
@media(max-width:760px){.sg-project__inner,.sg-showcase__inner{grid-template-columns:1fr}.sg-project:nth-of-type(even) .sg-project__image{order:0}.sg-project__copy h2{font-size:34px}.sg-project__copy dl{gap:16px}.sg-showcase img{max-height:320px}.nav__panel,.menu{overscroll-behavior:contain}}
@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto!important}.reveal{opacity:1!important;transform:none!important;clip-path:none!important}}

.hero__film .hero__fallback{position:absolute!important;inset:0!important;display:block!important;width:100%!important;height:100%!important;object-fit:cover!important;object-position:var(--focal,50% 50%)!important;z-index:0!important}.hero__film>video{position:absolute;inset:0;z-index:1} .hero__film::after{z-index:3}.hero__film>.hero__ios-reel{z-index:2}@media(max-width:759px){.hero__film .hero__fallback{object-position:var(--focal-m,50% 38%)!important}}
@media(prefers-reduced-motion:reduce){.reveal,.rise-mask>span{animation:none!important;transition:none!important;opacity:1!important;transform:none!important;clip-path:none!important}}

.foot__tagline:empty,.foot__division:empty{display:none}

.seo-footer__tagline:empty,.seo-footer__division:empty,.seo-footer__availability:empty{display:none}
.seo-footer__location{max-width:34ch;line-height:1.7}

@view-transition{navigation:auto}
::view-transition-old(root),::view-transition-new(root){animation-duration:.24s;animation-timing-function:cubic-bezier(.22,.61,.36,1)}
::view-transition-group(site-header){animation-duration:.24s}
::view-transition-old(site-header),::view-transition-new(site-header){animation:none;mix-blend-mode:normal}
html.js-reveal .phead .reveal,html.js-reveal .hero .reveal,html.js-reveal .phead .rise-mask>span,html.js-reveal .hero .rise-mask>span{opacity:1;transform:none}
html.page-arriving .phead>.wrap,html.page-arriving .hero__inner{animation:page-arrive .38s cubic-bezier(.22,.61,.36,1) both}
@keyframes page-arrive{from{opacity:.9;transform:translateY(6px)}to{opacity:1;transform:none}}
@media(prefers-reduced-motion:reduce){@view-transition{navigation:none}.callbar,.ag-launch{transition:none!important}.callbar__btn--primary::after{animation:none}.reveal-line{transform:none!important}html.page-arriving .phead>.wrap,html.page-arriving .hero__inner{animation:none}}

/* Header surface, edge and departure ---------------------------------------
   The bar is treated as a surface held just above the page rather than a panel
   laid on top of it. Three things carry that: a warm veil that thins downward
   so the tone is densest where the wordmark and the control sit, a two-part
   edge (hairline plus one short contact falloff) consistent with light from
   above, and an exit in which the contents leave before the surface does. */
.nav {
  box-shadow: 0 0 0 0 rgba(36, 31, 24, 0);
  transition:
    transform .4s cubic-bezier(.17, .89, .32, 1.02),
    opacity .24s linear,
    background-color .45s cubic-bezier(.22, .61, .36, 1),
    box-shadow .45s cubic-bezier(.22, .61, .36, 1),
    border-color .45s cubic-bezier(.22, .61, .36, 1) !important;
}
.nav__inner {
  transition:
    transform .42s cubic-bezier(.17, .89, .32, 1.02) .05s,
    opacity .3s linear .05s;
}
.nav--stuck {
  border-bottom-color: transparent !important;
  box-shadow:
    0 1px 0 0 rgba(36, 31, 24, .07),
    8px 16px 37px -28px rgba(36, 31, 24, .5);
}
/* Wide layout only: below 1201px the veil lives on the bar's ::before. */
@media (min-width: 1201px) {
  .nav--stuck {
    background: linear-gradient(180deg,
        rgba(233, 227, 213, .962) 0%,
        rgba(233, 227, 213, .94) 62%,
        rgba(233, 227, 213, .912) 100%) !important;
  }
}

/* Where the compositor can blur the backdrop, the veil can be thinner: the
   blur removes the detail behind the bar, so the tone alone carries legibility
   and the surface reads as translucent rather than as a fill.
   Held to the wide layout: a backdrop filter or a promoted transform makes the
   bar the containing block for fixed descendants, and below 1201px the menu
   panel is a fixed, full-screen child of the bar. */
@media (min-width: 1201px) {
  .nav { will-change: transform, opacity; }
}
@supports ((backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px))) {
  @media (pointer: fine) and (min-width: 1201px) {
    .nav--stuck {
      background: linear-gradient(180deg,
          rgba(233, 227, 213, .92) 0%,
          rgba(233, 227, 213, .902) 62%,
          rgba(233, 227, 213, .885) 100%) !important;
      -webkit-backdrop-filter: blur(18px) saturate(1.04) !important;
      backdrop-filter: blur(18px) saturate(1.04) !important;
    }
  }
}

/* Touch devices keep an unblurred veil: a backdrop filter on a fixed bar costs
   frames on every scroll, and frame pacing outranks the effect.

   Two states. At rest over the hero the bar has no edge: the veil stays dense
   through the lockup and thins away below it, so the image comes up through
   the bar instead of meeting a cut line. Once scrolled, the bar is over
   content and has to separate from it, so the lower veil closes up, the soft
   bleed gives way and a hairline with a short contact falloff appears. The
   stops are registered properties so the change is a transition, not a snap. */
@property --nav-veil-mid { syntax: "<number>"; inherits: false; initial-value: .89; }
@property --nav-veil-low { syntax: "<number>"; inherits: false; initial-value: .82; }
@property --nav-veil-floor { syntax: "<number>"; inherits: false; initial-value: .66; }
@media (max-width: 1200px) {
  .nav::before,
  .nav--stuck::before {
    --nav-veil-mid: .89;
    --nav-veil-low: .82;
    --nav-veil-floor: .66;
    background: linear-gradient(180deg,
        rgba(233, 227, 213, .92) 0%,
        rgba(233, 227, 213, var(--nav-veil-mid)) 52%,
        rgba(233, 227, 213, var(--nav-veil-low)) 76%,
        rgba(233, 227, 213, var(--nav-veil-floor)) 100%) !important;
    transition:
      opacity .35s cubic-bezier(.22, .61, .36, 1),
      --nav-veil-mid .45s cubic-bezier(.22, .61, .36, 1),
      --nav-veil-low .45s cubic-bezier(.22, .61, .36, 1),
      --nav-veil-floor .45s cubic-bezier(.22, .61, .36, 1);
  }
  .nav--stuck::before {
    --nav-veil-mid: .94;
    --nav-veil-low: .93;
    --nav-veil-floor: .92;
  }
  .nav::after {
    height: 24px;
    /* Picks up exactly where the veil ends, so there is no step to read as a line. */
    background: linear-gradient(180deg,
        rgba(233, 227, 213, .64) 0%,
        rgba(233, 227, 213, .34) 35%,
        rgba(233, 227, 213, .1) 70%,
        rgba(233, 227, 213, 0) 100%);
    transition: opacity .45s cubic-bezier(.22, .61, .36, 1);
  }
  .nav--stuck::after { opacity: 0; }
  .nav--stuck {
    box-shadow:
      0 1px 0 0 rgba(36, 31, 24, .06),
      7px 14px 32px -25px rgba(36, 31, 24, .46);
  }
}

/* Leaving: the contents fade and lift first, then the surface follows on a
   curve that starts slowly and accelerates away. Arriving reverses it — a
   shorter, decelerating move that settles. */
.nav--hidden {
  transform: translateY(-100%) !important;
  opacity: 0;
  pointer-events: none;
  box-shadow: 0 0 0 0 rgba(36, 31, 24, 0) !important;
  transition:
    transform .52s cubic-bezier(.4, 0, .68, .06),
    opacity .3s cubic-bezier(.4, 0, 1, 1) .1s,
    box-shadow .24s linear,
    background-color .24s linear !important;
}
.nav--hidden .nav__inner {
  opacity: 0;
  transition:
    transform .34s cubic-bezier(.45, 0, .75, 0),
    opacity .2s linear;
}
@media (min-width: 1201px) {
  .nav--hidden .nav__inner { transform: translateY(-7px); }
}
.nav--open.nav--hidden {
  transform: none !important;
  opacity: 1;
  pointer-events: auto;
}
.nav--open.nav--hidden .nav__inner {
  transform: none;
  opacity: 1;
}

/* Brand lockup. The mark carries about a sixth of its mass below its own
   centre, so centring the box leaves it reading low against the wordmark;
   the nudge puts the two optical centres on one line. */
.brand {
  align-items: center;
  gap: clamp(10px, .82vw, 13px);
}
.brand__logo {
  transform: translateY(-3px);
}

/* The menu control reads as a small raised tile: a lighter face than the bar, a
   firmer edge, a top highlight and a short contact shadow falling down-right
   with the rest of the page's light. The 44px target is unchanged. */
.nav__toggle {
  background: rgba(245, 241, 232, .78);
  border-color: rgba(36, 31, 24, .28);
  box-shadow:
    3px 6px 14px -9px rgba(36, 31, 24, .3),
    inset 0 1px 0 rgba(255, 255, 255, .55);
  transition:
    background-color .3s cubic-bezier(.22, .61, .36, 1),
    border-color .3s cubic-bezier(.22, .61, .36, 1),
    box-shadow .3s cubic-bezier(.22, .61, .36, 1),
    transform .22s cubic-bezier(.22, .61, .36, 1);
}
.nav__toggle:hover {
  background: rgba(245, 241, 232, .9);
  border-color: rgba(36, 31, 24, .36);
}
.nav__toggle:active {
  transform: scale(.965);
}
.nav__toggle span,
.nav__toggle span::before,
.nav__toggle span::after {
  width: 22px;
}
.nav__toggle span::before { top: -6px; }
.nav__toggle span::after { top: 6px; }
.nav--open .nav__toggle span::before { transform: translateY(6px) rotate(45deg); }
.nav--open .nav__toggle span::after { transform: translateY(-6px) rotate(-45deg); }

@media (prefers-reduced-motion: reduce) {
  .nav,
  .nav--hidden,
  .nav__inner,
  .nav--hidden .nav__inner,
  .nav__toggle {
    transition: none !important;
  }
  .nav--hidden .nav__inner { transform: none; }
}

/* Hover and current-page links sit on the translucent bar, and the hero behind
   it is near-black in places, so they use the palette-derived accent defined
   below, held a shade under the accent itself. */
.nav .nav__link:hover,
.nav .nav__link[aria-current="page"] { color: var(--nav-accent) !important; }
@media (min-width: 1201px) {
  .nav .nav__link::after { background: var(--nav-accent); }
}

/* Palette-aware header ------------------------------------------------------
   The veil and the control take their tone from the active palette, so the bar
   carries the same colour temperature as the page beneath it whichever palette
   is chosen. The link accent follows the palette too, held a shade darker than
   the accent itself because it sits on the thinnest part of the veil. */
.nav { --nav-accent: var(--accent); }
@supports (color: color-mix(in srgb, red 50%, blue)) {
  .nav { --nav-accent: color-mix(in oklab, var(--accent) 97%, #000); }
  @media (min-width: 1201px) {
    .nav--stuck {
      background: linear-gradient(180deg,
          color-mix(in srgb, var(--sand) 96.2%, transparent) 0%,
          color-mix(in srgb, var(--sand) 94%, transparent) 62%,
          color-mix(in srgb, var(--sand) 91.2%, transparent) 100%) !important;
    }
  }
  .nav__toggle { background: color-mix(in srgb, var(--marble) 78%, transparent); }
  .nav__toggle:hover { background: color-mix(in srgb, var(--marble) 90%, transparent); }
}
@supports (color: color-mix(in srgb, red 50%, blue)) and ((backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px))) {
  @media (pointer: fine) and (min-width: 1201px) {
    .nav--stuck {
      background: linear-gradient(180deg,
          color-mix(in srgb, var(--sand) 92%, transparent) 0%,
          color-mix(in srgb, var(--sand) 90.2%, transparent) 62%,
          color-mix(in srgb, var(--sand) 88.5%, transparent) 100%) !important;
    }
  }
}
@supports (color: color-mix(in srgb, red 50%, blue)) {
  @media (max-width: 1200px) {
    .nav::before,
    .nav--stuck::before {
      background: linear-gradient(180deg,
          color-mix(in srgb, var(--sand) 92%, transparent) 0%,
          color-mix(in srgb, var(--sand) calc(var(--nav-veil-mid) * 100%), transparent) 52%,
          color-mix(in srgb, var(--sand) calc(var(--nav-veil-low) * 100%), transparent) 76%,
          color-mix(in srgb, var(--sand) calc(var(--nav-veil-floor) * 100%), transparent) 100%) !important;
    }
    .nav::after {
      background: linear-gradient(180deg,
          color-mix(in srgb, var(--sand) 64%, transparent) 0%,
          color-mix(in srgb, var(--sand) 34%, transparent) 35%,
          color-mix(in srgb, var(--sand) 10%, transparent) 70%,
          transparent 100%);
    }
  }
}

/* Material captions ----------------------------------------------------------
   The second caption line names the stone and the finish — the facts a visitor
   is weighing — so it keeps the muted ink at full strength: 4.9–6.3:1 on every
   page ground, still a clear step below the heavier, darker title line. */
.shot__cap span { opacity: 1; }

/* Studio plates ---------------------------------------------------------------
   The plates are dark, so their caption takes the light gold the plates already
   use for their rings, not the dark accent meant for light grounds. Eighty per
   cent keeps it quieter than the artwork (4.6:1 at the plate's lit centre). The
   plate numeral stays an engraved ghost at the same faintness it has on light
   plates. */
.stu-plate__cap { color: rgba(194, 170, 126, .8); }
.stu-plate__n { color: rgba(194, 170, 126, .08); }
@supports (color: color-mix(in srgb, red 50%, blue)) {
  .stu-plate__cap { color: color-mix(in srgb, var(--stu-gold-lite) 80%, transparent); }
  .stu-plate__n { color: color-mix(in srgb, var(--stu-gold-lite) 8%, transparent); }
}

/* Ambient loops ---------------------------------------------------------------
   Loops that share a period, or sit at a simple ratio, fall into step and the
   combined motion starts to read as mechanical. Each is moved just enough to
   stay out of step with its neighbours; the movement itself is unchanged. */
.stu-drift { animation-duration: 37s; }
@media (max-width: 760px) {
  .art .tail-sway,
  .art .tail-drift,
  .panel__art .tail-sway,
  .panel__art .tail-drift { animation-duration: 7.7s !important; }
}

/* The call bar's sheen keeps its 1.56 s sweep and gains rest time so it no
   longer beats in step with the assistant mark beside it. */
.callbar__btn--primary::after { animation-duration: 6.8s; }
@keyframes callbar-sheen { 0% { transform: translateX(-120%); } 22.9%, 100% { transform: translateX(120%); } }

/* Layout containment only: the name label and the ring both sit outside the
   button's circle, and paint containment would clip them. */
.ag-launch { contain: layout; }
/* The label takes the assistant panel's charcoal, which is defined on the panel
   and not on the button beside it, so it needs the value carried here. */
.ag-launch__t { background: var(--ag-charcoal, #181510); }

/* The assistant ring keeps its 3.15 s expansion and now rests for five seconds
   between pulses: an occasional cue rather than a constant beat. */
.ag-launch::after { animation-duration: 8.6s; }
@keyframes ag-ring {
  0%   { opacity: .55; transform: scale(1); }
  36.6%, 100% { opacity: 0; transform: scale(1.55); }
}

/* Touch screens have no hover to reveal the button's name before the decision to
   tap, so it is shown once per visit, long enough to read. */
@media (hover: none) {
  .ag-launch--intro .ag-launch__t {
    display: block;
    animation: ag-launch-intro 4.6s cubic-bezier(.16, 1, .3, 1) 1.1s both;
  }
}
@keyframes ag-launch-intro {
  0%        { opacity: 0; transform: translateY(-50%) translateX(6px); }
  12%, 82%  { opacity: 1; transform: translateY(-50%) translateX(0); }
  100%      { opacity: 0; transform: translateY(-50%) translateX(4px); }
}
@media (hover: none) and (prefers-reduced-motion: reduce) {
  .ag-launch--intro .ag-launch__t { animation: ag-launch-intro-still 4.6s linear 1.1s both; }
}
@keyframes ag-launch-intro-still {
  0%, 97%  { opacity: 1; transform: translateY(-50%); }
  100%     { opacity: 0; transform: translateY(-50%); }
}

/* Leaving through the menu ----------------------------------------------------
   The chosen link takes the accent and its chevron steps forward; the others
   step back; a hairline under the choice fills on a decelerating curve and stops
   short of the edge, since it marks that the page is on its way, not how far.
   The bar leaves the shared header transition so the whole menu dissolves into
   the next page rather than vanishing ahead of it. */
@media (max-width: 1200px) {
  .nav__link {
    background: linear-gradient(var(--nav-accent), var(--nav-accent)) left bottom / 0 1px no-repeat;
    background-origin: border-box;
    background-clip: border-box;
    transition: opacity .26s cubic-bezier(.22, .61, .36, 1), color .26s cubic-bezier(.22, .61, .36, 1);
  }
  .nav--leaving .nav__link:not(.is-chosen),
  .nav--leaving .btn--nav { opacity: .34; }
  .nav--leaving .nav__link.is-chosen {
    color: var(--nav-accent) !important;
    animation: nav-leave-line 2.8s cubic-bezier(.16, 1, .3, 1) forwards;
  }
  .nav--leaving .nav__link.is-chosen::after {
    opacity: .9;
    transform: translateX(4px) rotate(45deg);
    transition: transform .42s cubic-bezier(.16, 1, .3, 1), opacity .26s linear;
  }
}
@keyframes nav-leave-line { from { background-size: 0 1px; } to { background-size: 88% 1px; } }
@supports (view-transition-name: none) {
  .nav--leaving { view-transition-name: none; }
}
@media (prefers-reduced-motion: reduce) {
  .nav--leaving .nav__link.is-chosen { animation: none; background-size: 88% 1px; }
  .nav--leaving .nav__link.is-chosen::after { transform: rotate(45deg); transition: none; }
}

/* Raised objects and the light ------------------------------------------------
   One light lights every page: above, to the upper left, 26 degrees from
   vertical. Each raised object has a height, --lift, and its whole shadow follows
   from that one number: the offset runs along the light, the penumbra widens and
   the umbra draws in as height grows, while the tight contact shade under the
   edge fades out. Edge darkness stays nearly constant and only the reach grows,
   which is how a broad light behaves, so a rising card reads as rising rather
   than as getting dirtier. The shade is the page's own ink with a little of its
   accent: light that has bounced between warm stone surfaces returns more
   saturated, not grey, and the tint follows whichever palette is chosen.
   Shadows move only when height changes - a card settling as it arrives, rising
   under the pointer, pressing under a finger. Scrolling does not move them:
   the light belongs to the page, not to the reader. */
@property --lift {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}
:root {
  --sg-lift-k: 1;
  --sg-shade-key: rgb(36 31 24 / .36);
  --sg-shade-ao: rgb(36 31 24 / .12);
}
@supports (color: color-mix(in srgb, red 50%, transparent)) {
  :root {
    --sg-shade: color-mix(in oklab, var(--ink, #241F18) 84%, var(--accent, #6D583B));
    --sg-shade-key: color-mix(in srgb, var(--sg-shade) 36%, transparent);
    --sg-shade-ao: color-mix(in srgb, var(--sg-shade) 12%, transparent);
  }
}
/* A CSS pixel subtends about a third more of the eye at handheld distance than
   at a desk, so heights are drawn smaller there to look the same. */
@media (pointer: coarse) {
  :root { --sg-lift-k: .76; }
}

:root .wall > .reveal,
:root .rail .card,
:root .way,
:root .stu-card,
:root .stu-panel,
:root .stu-metric,
:root .stu-plate {
  --lift: 1.5;
  box-shadow:
    calc(var(--lift) * var(--sg-lift-k) * .4877px) calc(var(--lift) * var(--sg-lift-k) * 1px) calc(var(--lift) * var(--sg-lift-k) * 2.08px) calc(var(--lift) * var(--sg-lift-k) * -1.23px) var(--sg-shade-key),
    0 calc(.5px + var(--lift) * .06px) calc(2px + var(--lift) * .3px) calc(var(--lift) * -.35px) var(--sg-shade-ao) !important;
}
:root .shot__frame {
  --lift: 20;
  box-shadow:
    0 1px 0 var(--rule-soft),
    calc(var(--lift) * var(--sg-lift-k) * .4877px) calc(var(--lift) * var(--sg-lift-k) * 1px) calc(var(--lift) * var(--sg-lift-k) * 2.08px) calc(var(--lift) * var(--sg-lift-k) * -1.23px) var(--sg-shade-key),
    0 calc(.5px + var(--lift) * .06px) calc(2px + var(--lift) * .3px) calc(var(--lift) * -.35px) var(--sg-shade-ao) !important;
}

/* The image in a wall tile is part of the tile, so the tile carries the height
   and the image no longer slides or casts a second shadow inside it. */
:root .wall .piece,
:root .wall .piece:hover,
:root .wall .piece:active {
  box-shadow: none !important;
  transform: none !important;
}

/* Every property a card animates is listed together, so the reveal no longer
   drops the hover and border transitions and the collection cards fade in
   instead of appearing at full strength. */
:root .wall > .reveal {
  transition: opacity .42s var(--ease), transform .42s var(--ease), border-color .4s var(--ease), --lift .42s var(--ease) !important;
}
:root .rail .card {
  transition: opacity .42s var(--ease), transform .42s var(--ease), --lift .42s var(--ease) !important;
}
:root .way {
  transition: opacity .42s var(--ease), transform .42s var(--ease), background-color .4s var(--ease), border-color .4s var(--ease), --lift .42s var(--ease) !important;
}
:root .stu-card {
  transition: opacity .42s var(--ease), transform .6s var(--ease), border-color .5s var(--ease), background-color .5s var(--ease), --lift .6s var(--ease) !important;
}
:root .stu-panel,
:root .stu-metric {
  transition: opacity .42s var(--ease), transform .42s var(--ease) !important;
}
html.js-reveal .wall > .reveal,
html.js-reveal .rail .card,
html.js-reveal .way,
html.js-reveal .stu-card,
html.js-reveal .stu-panel,
html.js-reveal .stu-metric {
  transition-delay: calc(var(--rd, 0s) * .35) !important;
}
html.js-reveal .wall > .reveal:is(:hover, :active),
html.js-reveal .rail .card:is(:hover, :active),
html.js-reveal .way:is(:hover, :active),
html.js-reveal .stu-card:is(:hover, :active) {
  transition-delay: 0s !important;
}

/* Arrival: a card fades into place and then settles the last distance onto the
   page. Seen from above, that descent shows only in its shadow, which draws in on
   a decelerating curve after the card itself is already still. */
@keyframes sg-land { from { --lift: 22; } }
@keyframes sg-land-high { from { --lift: 42; } }
html.js-reveal .wall > .reveal.is-in,
html.js-reveal .rail .card.is-in,
html.js-reveal .way.is-in,
html.js-reveal .stu-panel.is-in,
html.js-reveal .stu-metric.is-in {
  animation: sg-land 1.4s cubic-bezier(.33, 1, .68, 1) calc(var(--rd, 0s) * .35) backwards;
}
html.js-reveal .shot.is-in .shot__frame {
  animation: sg-land-high 1.8s cubic-bezier(.33, 1, .68, 1) calc(var(--rd, 0s) * .35) backwards;
}
/* The studio cards keep their rise; holding its end state no longer overrides
   the hover lift the cards were designed with. */
html.js-reveal .stu-card.is-in {
  animation:
    stu-card-in 1s var(--ease, cubic-bezier(.16, 1, .3, 1)) backwards,
    sg-land 1.5s cubic-bezier(.33, 1, .68, 1) backwards !important;
}

/* A plate is set down on the bench each time it comes into view. */
:root .stu-plate {
  --lift: 4;
  transition: --lift 1.4s cubic-bezier(.16, 1, .3, 1);
}
html.js-reveal .stu-dept:not(.is-bench) .stu-plate { --lift: 16; }

/* Pointer: the card and its shadow rise together, on the same curve. */
@media (hover: hover) and (pointer: fine) {
  :root .wall > .reveal:hover { --lift: 22; transform: translateY(-3px) !important; }
  :root .rail .card:hover { --lift: 14; }
  :root .way:hover { --lift: 16; transform: translateY(-3px) !important; }
  :root .stu-card:hover { --lift: 20; transform: translate3d(0, -7px, 0) !important; }
}

/* Touch: cards that open something rest a little higher, since without hover
   the resting shadow is the only sign they can be pressed; a press sets them
   down. Cards that only hold text stay flat, so height is never a false
   promise. Hover states that touch browsers leave stuck after a tap are
   cancelled. */
@media (hover: none) {
  :root .wall > .reveal,
  :root .rail .card,
  :root .way,
  :root a.stu-card { --lift: 5; }
  :root .wall > .reveal:active,
  :root .rail .card:active,
  :root .way:active,
  :root a.stu-card:active {
    --lift: 1;
    transform: translate3d(0, 1px, 0) !important;
  }
  :root .rail .card:hover:not(:active),
  :root .stu-card:hover:not(:active) { transform: none !important; }
}

/* The collection rail clips what it scrolls, so it gains room for a lifted
   card's shadow without moving anything around it. */
:root .rail {
  padding-top: 8px;
  margin-top: -8px;
  padding-bottom: 36px;
  margin-bottom: -14px;
}

/* An opened piece rises off the page toward the viewer. */
.lb .lb__img {
  --lift: 12;
  box-shadow: calc(var(--lift) * var(--sg-lift-k) * .4877px) calc(var(--lift) * var(--sg-lift-k) * 1px) calc(var(--lift) * var(--sg-lift-k) * 2.25px) calc(var(--lift) * var(--sg-lift-k) * -1px) rgb(0 0 0 / .9);
  transition: opacity .28s var(--ease), filter .32s var(--ease), --lift 1.1s cubic-bezier(.16, 1, .3, 1);
}
.lb.is-open .lb__img { --lift: 40; }

/* The still setting holds every object at its resting height. */
html[data-motion="still"] :is(.wall > .reveal, .rail .card, .way, .stu-card, .stu-panel, .stu-metric, .stu-plate, .shot__frame, .lb__img) {
  transition: none !important;
  animation: none !important;
}

/* Phones held sideways ---------------------------------------------------------
   With about 320-430 px of height, the header and the call bar together covered
   a third or more of the screen whenever both were up. The header gives back
   its vertical padding, the call bar steps aside while reading downward and
   returns with the header, and the menu lays its links in two columns so the
   whole list and the enquiry button fit without scrolling. Every target keeps a
   44 px height. */
@media (orientation: landscape) and (max-height: 500px) and (pointer: coarse) {
  .nav { padding-block: 8px; }
  .nav__links {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: clamp(24px, 5vw, 48px);
    align-content: start;
    padding-top: 74px;
  }
  .nav__link { padding-block: 13px; }
  .nav__links .btn--nav { grid-column: 1 / -1; margin-top: 18px; }
  body:has(.nav--hidden:not(.nav--open)) .callbar.is-up {
    transform: translateY(115%);
    visibility: hidden;
    pointer-events: none;
    transition: transform .34s cubic-bezier(.45, 0, .75, 0), visibility 0s linear .34s;
  }
  body:has(.nav--hidden:not(.nav--open)) .ag-launch {
    bottom: calc(clamp(14px, 2vw, 26px) + env(safe-area-inset-bottom, 0px));
  }
}

/* Line length ------------------------------------------------------------------
   Past about 75 characters the eye starts to miss the start of the next line on
   the return sweep. These typefaces average 0.44em a character, so running text
   is held to 34em wherever its column is wider; phones are already narrower. */
:root :is(.body, .lede, .proc__d, .trio__d, .tl__d, .post__x, .acc__inner, .stu-prin__b, .stu-area__b, .stu-card__d, .stu-lede, .stu-head__aside, .stu-dept__b) {
  max-inline-size: 34em;
}

/* Reading size by device ------------------------------------------------------
   This block used to zoom running text 10 per cent on high-density
   pointer-fine screens between 1100px and 1800px, to compensate for the
   greater viewing distance of a laptop. The instinct was right and the
   instrument was wrong: a step is discontinuous, so text grew as the window
   widened past 1100px and shrank again past 1800px --

       1099px  18.77px        1800px  24.20px
       1100px  20.65px        1801px  22.00px

   -- and once the reading size became a continuous clamp solved against
   measured devices, the two corrections compounded on exactly that band. The
   clamp near the end of this file is the whole of the correction now. */

/* Touch targets ----------------------------------------------------------------
   A fingertip covers roughly 9 mm, about 44 CSS px. Small links keep their look
   and their place in the layout; only the area that answers a tap grows, and
   never so far that it reaches a neighbouring link. */
@media (pointer: coarse) {
  .foot__social a,
  .foot__legal a,
  .foot__list a,
  .foot__mail,
  .phead__crumb a,
  .ilink,
  .tlink { position: relative; }
  .foot__social a::before,
  .foot__legal a::before,
  .foot__list a::before,
  .foot__mail::before,
  .phead__crumb a::before,
  .ilink::before,
  .tlink::before {
    content: "";
    position: absolute;
  }
  .foot__social a::before { inset: -5px; }
  .foot__legal a::before,
  .phead__crumb a::before { inset: -13px -6px; }
  .foot__list a::before { inset: -9px -6px; }
  .foot__mail::before { inset: -11px -5px; }
  .foot__mail:has(+ .foot__mail) { margin-right: 14px; }
  .ilink::before { inset: -12px -6px; }
  .tlink::before { inset: max(-9px, calc((44px - 100%) / -2)) -4px; }
}

/* Collection numerals sit on photographs of every tone; on a pale product shot
   the white figures vanished. A close smoky halo gives them a dark ground of
   their own that is lost against dark photographs and holds them on light ones. */
.card__no {
  text-shadow:
    0 0 2px rgba(20, 17, 12, .75),
    0 0 6px rgba(20, 17, 12, .6),
    0 0 12px rgba(20, 17, 12, .5),
    0 0 22px rgba(20, 17, 12, .35);
}

/* ==========================================================================
   Tablet geometry (V51)

   Between the phone rules (<= 760px) and the desktop composition (>= 1200px)
   the layout had no band of its own: the page-header artwork kept its desktop
   anchor in the right margin while the text column widened to nearly the whole
   viewport, so on an iPad the headings and ledes ran straight across the
   drawings. The block below gives that range an explicit geometry.

   The corridor
   ------------
   For a page header the art is pinned to the right edge. Its left edge sits at

       x_art = W - R - A

   where W is the viewport, R the right offset and A the art width. Text must
   stop a gap G before that, so the wrap needs an end padding of

       P = A + R + G

   measured from the wrap's own right edge, which equals the viewport edge for
   every width below --wrap (1400px). Both the art and the padding read the same
   three custom properties, so the corridor cannot drift out of step.

   At 768px:  A = 176.6  R = 24.6  G = 20   -> P = 221.2, text column 512px
   At 1024px: A = 235.5  R = 32.8  G = 26.6 -> P = 294.9, text column 683px
   At 1199px: A = 275.8  R = 38.4  G = 31.2 -> P = 345.4, text column 800px
   ========================================================================== */
@media (min-width: 761px) and (max-width: 1199.98px) {
  .phead {
    --sg-art-w: clamp(168px, 23vw, 300px);
    --sg-art-right: 3.2vw;
    --sg-art-gap: clamp(20px, 2.6vw, 34px);
  }
  .phead__art {
    width: var(--sg-art-w) !important;
    max-width: none !important;
    right: var(--sg-art-right) !important;
    left: auto !important;
    opacity: .72;
  }
  .phead > .wrap {
    padding-inline-end: calc(var(--sg-art-w) + var(--sg-art-right) + var(--sg-art-gap));
  }
  /* The headings and ledes keep their own ch caps. The corridor narrows the
     column itself, so whichever of the two is tighter already wins -- overriding
     the caps here would only widen the lede past its reading measure. */
}

/* --------------------------------------------------------------------------
   The type ramp was continuous nowhere near the tablet range: parity.css runs
   a phone scale (7.4vw and friends) all the way to 1023px, and site.css takes
   over at 1024px with a desktop scale. A display heading therefore jumped from
   75.7px at 1023px down to 57.3px at 1024px -- an 18px step across one pixel of
   viewport, which is the gap between an iPad in portrait and the same iPad in
   landscape.

   Each size below interpolates linearly between the phone value at 760px and
   the desktop value at 1200px, so the two scales meet without a step:

       f(W) = f(760) + (W - 760) * (f(1200) - f(760)) / 440

   d-xxl  56.24 -> 67.2    d-xl  47.12 -> 45.6
   d-lg   41.04 -> 32.4    d-md  33.44 -> 24
   -------------------------------------------------------------------------- */
@media (min-width: 761px) and (max-width: 1199.98px) {
  .display.d-xxl { font-size: calc(37.31px + 2.4909vw); }
  .display.d-xl  { font-size: calc(49.75px - 0.3455vw); }
  .display.d-lg  { font-size: calc(55.96px - 1.9636vw); }
  .display.d-md  { font-size: calc(49.75px - 2.1455vw); }
}

/* --------------------------------------------------------------------------
   Collections / "Sacred spaces": the archway is anchored to the left gutter,
   which is where the section's own text starts at every width. On desktop it
   ran under the spec keys (STONE, FORMAT, ...); below 1200px the grid collapses
   to one column and the artwork ended up beneath the eyebrow, the heading and
   the link at once -- the worst collision on the site.

   Tablet moves it into the same right-hand corridor the page headers use.

   Desktop needed the second answer. The heading block of this section sits at
   grid-column 5 / span 8, so the left third belongs to the archway -- but the
   spec list underneath spanned the full wrap, putting its key column straight
   through the drawing at every width from 1200px up.

   Aligning the list with the heading closes it exactly. For a twelve-column
   grid of content width C and gutter g, one column is (C - 11g)/12, and a
   block starting at column 5 is offset by four columns and four gutters:

       offset = 4 * (C - 11g)/12 + 4g = (C + g)/3

   which is a third of the content width plus a third of one gutter, and needs
   no magic number: it follows the grid at any width.
   -------------------------------------------------------------------------- */
@media (min-width: 1200px) {
  #sacred > .wrap > .spec {
    margin-left: calc((100% + var(--gut)) / 3);
  }
}
@media (min-width: 761px) and (max-width: 1199.98px) {
  /* A corridor this narrow would leave the archway too small to read, so the
     drawing is wider than the corridor and runs off the right edge, which the
     band already clips. The corridor formula is unchanged -- a negative right
     offset simply makes P smaller than A:

         P = A + R + G   with R < 0   ->   visible width = A + R

     At 768px: A = 230.4, R = -46.1, G = 20 -> 184px of archway, 204px reserved. */
  #sacred {
    --sg-art-w: clamp(220px, 30vw, 340px);
    --sg-art-right: -6%;
    --sg-art-gap: clamp(20px, 2.6vw, 34px);
  }
  #sacred .art[data-art="rajasthan-archway"] {
    width: var(--sg-art-w) !important;
    max-width: none !important;
    max-height: none !important;
    left: auto !important;
    right: var(--sg-art-right) !important;
    top: 50% !important;
    transform: translateY(-50%) !important;
  }
  #sacred > .wrap {
    padding-inline-end: calc(var(--sg-art-w) + var(--sg-art-right) + var(--sg-art-gap));
  }
}

/* --------------------------------------------------------------------------
   Portfolio: the peacock sits at right:2% beside running body copy. At desktop
   the copy stops well short of it; on a tablet the lines reach into its tail.
   Shrinking it and taking it to the edge restores the clearance without losing
   the bird.
   -------------------------------------------------------------------------- */
@media (min-width: 761px) and (max-width: 1199.98px) {
  .art[data-art="peacock"] {
    width: clamp(110px, 14vw, 180px) !important;
    right: 0 !important;
    left: auto !important;
    opacity: .6;
  }
}

/* --------------------------------------------------------------------------
   Global: the jharokha is the one page-header drawing wide enough to still
   catch its heading between 1200px and 1400px, where the wrap has not yet
   gained outer margins. Trimming it is enough; the desktop composition above
   1400px is untouched.
   -------------------------------------------------------------------------- */
@media (min-width: 1200px) and (max-width: 1399.98px) {
  .phead__art[data-art="rajasthan-jharokha"] {
    width: min(22vw, 262px) !important;
    right: 2.4% !important;
  }
}

/* --------------------------------------------------------------------------
   Two grids that were sized for a wider viewport than they were given.

   .feature -- parity.css re-splits the twelve-column feature into 7 media / 5
   text between 700px and 1000px, although components.css had already stacked it
   for everything under 1100px. The narrower viewport therefore got the
   side-by-side layout and the wider one got the stacked one. Five of twelve
   columns is

       text = 5 * (C - 11g) / 12 + 4g

   which at 768px (C = 699, g = 24) leaves 277px, about 24 characters a line.
   Holding a 45-character minimum needs text >= 374px, i.e. C >= 931px, i.e. a
   viewport of 1023px. Below that the stack is the only honest layout.

   .process -- an inline repeat(3) survives down to 768px, where each card is
   232px and its description 172px: 20 characters a line. A card needs about
   314px (282px of text plus 2 x 16px of padding), so three of them need a
   container of 942px -- a viewport of 1040px. Below that, two.
   -------------------------------------------------------------------------- */
@media (max-width: 1023.98px) {
  .feature__media,
  .feature__text,
  .feature--flip .feature__media,
  .feature--flip .feature__text {
    grid-column: 1 / -1;
    grid-row: auto;
  }
  .feature__stamp {
    position: static;
    max-width: none;
    margin-top: 18px;
  }
}

@media (min-width: 761px) and (max-width: 1039.98px) {
  .process,
  .process[style*="grid-template-columns"],
  .process[style*="repeat(3"],
  .process[style*="repeat(4"] {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }
  .process > :last-child:nth-child(odd) { grid-column: 1 / -1; }
}

/* A pasted film or post fills its frame exactly as an uploaded one does, so a
   page does not change shape depending on where the work happens to be kept. */
.sg-presentation__inner > .sg-embed {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  background: var(--stone-900, #1E1A14);
}

/* ==========================================================================
   The reading size (V55)

   Reading performance is governed by the angle a letter subtends at the eye,
   not by the number of pixels it is drawn with. The same 15.68px is 0.24° of
   x-height on a phone held at 32cm and 0.157° on a 27-inch monitor at 68cm,
   because a CSS pixel is a different physical length on each.

   Measured across 21 real devices -- published display widths, published
   viewing distances, and the x-height ratio of Instrument Sans measured from
   the rendered glyph rather than assumed -- the fixed size put six of them
   below the critical print size, the size beneath which reading speed falls
   away from its maximum. Laptops were the worst: a dense CSS pixel at a long
   viewing distance. Phones were the best, because they are held close.

   Solving for the clamp that maximises the worst device, subject to nothing
   falling below threshold and a ceiling the brand can carry:

       worst case  0.157° -> 0.22°      below threshold  6 of 21 -> 0 of 21

   Phones keep the size they already had; the correction lands where the
   deficit was. Running prose is unified onto one size, which is what the
   difference between 15.36, 15.68 and 17px was always approximating.
   ========================================================================== */
:root {
  --sg-read: clamp(15.5px, 13px + 0.525vw, 22px);
}
body {
  font-size: var(--sg-read);
}
.lede {
  font-size: calc(var(--sg-read) * 1.12);
}
.tl__d,
.post__x,
.post__full p,
.body,
.proc__d {
  font-size: var(--sg-read);
}

/* --------------------------------------------------------------------------
   The corridor, continued past 1200px.

   Below 1200px the page-header art is held in a reserved corridor. Above it,
   the art returned to its full desktop width while the text column ran free,
   and the two were kept apart only by the headings happening to be short. The
   larger reading size removed that slack: at 1200px the portfolio art is 528px
   wide and the lede reached 93px past its left edge.

   The reservation is the same identity as before,

       P = A + R + G - M

   with M the space outside the wrap once the viewport passes --wrap (1400px),
   since padding inside the wrap is measured from the wrap's edge, not the
   window's. A is per page because each header carries a different drawing;
   the two pages sharing a drawing take the larger of the two widths, which
   over-reserves by a few pixels on one of them and is never wrong.
   -------------------------------------------------------------------------- */
@media (min-width: 1200px) {
  .phead {
    --sg-art-right: 4vw;
    --sg-art-gap: clamp(24px, 2.4vw, 44px);
    --sg-wrap-edge: max(0px, calc((100vw - 1400px) / 2));
    --sg-art-w: 0px;
  }
  .phead:has(.phead__art[data-art="lotus-mandala"])      { --sg-art-w: min(36vw, 440px); }
  .phead:has(.phead__art[data-art="rajasthan-jharokha"]) { --sg-art-w: min(28vw, 332px); }
  .phead:has(.phead__art[data-art="heritage-tools"])     { --sg-art-w: min(42vw, 540px); }
  .phead:has(.phead__art[data-art="stone-artisan"])      { --sg-art-w: min(44vw, 580px); }
  .phead > .wrap {
    padding-inline-end: max(
      clamp(20px, 4.5vw, 68px),
      calc(var(--sg-art-w) + var(--sg-art-right) + var(--sg-art-gap) - var(--sg-wrap-edge))
    );
  }
}

/* --------------------------------------------------------------------------
   The type floors, carried past 1023px.

   parity.css sets a minimum size for every small label and applies it only
   below 1024px. Above that the floor disappears, and the same labels render
   between 8.8px and 10.9px -- on the screens whose readers sit furthest away.
   A 27-inch monitor is about twice the viewing distance of a phone, so a
   label needs more pixels there to subtend the same angle at the eye, not
   fewer.

   These are the same floors, at the widths they were missing from. No size
   here is invented: each is the one the design already chose for itself.
   -------------------------------------------------------------------------- */
@media (min-width: 1024px) {
  .piece__m { font-size: max(0.82rem, 0.46rem); letter-spacing: 0.13em; }
  .brand__sub { font-size: max(0.82rem, 0.5rem); letter-spacing: 0.13em; }
  .pcard__badge { font-size: max(0.82rem, 0.53rem); letter-spacing: 0.13em; }
  .specs__l { font-size: max(0.82rem, 0.54rem); letter-spacing: 0.13em; }
  .card__k { font-size: max(0.82rem, 0.55rem); letter-spacing: 0.13em; }
  .card__no { font-size: max(0.82rem, 0.55rem); letter-spacing: 0.13em; }
  .cdl__k { font-size: max(0.82rem, 0.55rem); letter-spacing: 0.13em; }
  .feature__stamp .mono { font-size: max(0.82rem, 0.55rem); letter-spacing: 0.13em; }
  .foot__col .foot__head { font-size: max(0.82rem, 0.55rem); letter-spacing: 0.13em; }
  .form__note { font-size: max(0.82rem, 0.55rem); letter-spacing: 0.13em; }
  .pcard__meta { font-size: max(0.82rem, 0.55rem); letter-spacing: 0.13em; }
  .post__tag { font-size: max(0.82rem, 0.55rem); letter-spacing: 0.13em; }
  .work__meta { font-size: max(0.82rem, 0.55rem); letter-spacing: 0.13em; }
  .market__d { font-size: max(0.82rem, 0.56rem); letter-spacing: 0.13em; }
  .pillar__i { font-size: max(0.82rem, 0.56rem); letter-spacing: 0.13em; }
  .plate__tag { font-size: max(0.82rem, 0.56rem); letter-spacing: 0.13em; }
  .proc__k { font-size: max(0.82rem, 0.56rem); letter-spacing: 0.13em; }
  .field label { font-size: max(0.82rem, 0.57rem); letter-spacing: 0.13em; }
  .phead__crumb { font-size: max(0.82rem, 0.57rem); letter-spacing: 0.13em; }
  .stat__l { font-size: max(0.82rem, 0.57rem); letter-spacing: 0.13em; }
  .lb__count { font-size: max(0.82rem, 0.58rem); letter-spacing: 0.13em; }
  .post__n { font-size: max(0.82rem, 0.6rem); letter-spacing: 0.13em; }
  .proc__list li { font-size: max(0.82rem, 0.6rem); letter-spacing: 0.13em; }
  .tl__yr { font-size: max(0.82rem, 0.62rem); letter-spacing: 0.13em; }
  .nav__link { font-size: max(0.82rem, 0.63rem); letter-spacing: 0.13em; }
  .tlink { font-size: max(0.82rem, 0.64rem); letter-spacing: 0.13em; }
  .btn { font-size: max(0.82rem, 0.66rem); letter-spacing: 0.13em; }
  .ticker__item { font-size: max(0.82rem, 0.66rem); letter-spacing: 0.13em; }
  .piece__below .piece__m { font-size: max(0.82rem, 0.7rem); letter-spacing: 0.13em; }
  .ag-head__s { font-size: max(11px, 0.5rem); }
  .ag-msg__k { font-size: max(11px, 0.5rem); }
  .ag-note { font-size: max(11px, 0.5rem); }
  .chan__k { font-size: max(11px, 0.5rem); }
  .piece__more { font-size: max(11px, 0.5rem); }
  .works > .work:not(:first-child):not(:last-child) .work__s, .works > .w-5 .work__s { font-size: max(11px, 0.52rem); }
  .spine__label { font-size: max(11px, 0.53rem); }
  .ag-chip { font-size: max(11px, 0.55rem); }
  .chan__addr-go { font-size: max(11px, 0.55rem); }
  .foot__bottom p, .foot__legal a, .foot__legal span { font-size: max(11px, 0.55rem); }
  .frame__note { font-size: max(11px, 0.55rem); }
  .ag-launch__t { font-size: max(11px, 0.56rem); }
  .callbar__btn { font-size: max(11px, 0.58rem); }
  .btn--nav { font-size: max(11px, 0.6rem); }
  .cn__eyebrow { font-size: max(11px, 0.6rem); }
  .foot__hours { font-size: max(11px, 0.6rem); }
  .way__go { font-size: max(11px, 0.6rem); }
  .stu-hero__sig p { font-size: max(11px, 0.62rem); }
  .stu-stat__l { font-size: max(11px, 0.62rem); }
  .stu-way__k { font-size: max(11px, 0.62rem); }
  .cwork__k { font-size: max(11px, 0.64rem); }
  .spec__k { font-size: max(11px, 0.64rem); }
  .shot__cap { font-size: max(11px, 0.66rem); }
  .toc__d { font-size: max(11px, 0.66rem); }
  .stu-eyebrow-row .stu-key { font-size: max(11px, 0.68rem); }
  .stu-key { font-size: max(11px, 0.68rem); }
  .stu-work__m { font-size: max(11px, 0.68rem); }
  .ilink { font-size: max(11px, 0.7rem); }
  .latest__m { font-size: max(11px, 0.7rem); }
  .toc__n { font-size: max(11px, 0.7rem); }
}


/* The studio plate caption was in no floor list at all, so it rendered at
   9px on every screen. It is real caption text, not ornament, so it takes
   the same minimum as the labels beside it. */
.stu-plate__cap { font-size: max(11px, 0.58rem); }

/* ==========================================================================
   The type scale, tied to the reading size

   The reading size now grows with the viewport, because a CSS pixel is a
   smaller angle at a greater viewing distance. The display sizes did not:
   each is a clamp with a rem ceiling, so above about 1400px they stop while
   the body text keeps going. The gap between the smallest heading and the
   body text closed as the screen got bigger:

       393px  d-sm / body = 1.29      1512px  1.09
       834px                1.15      2560px  1.05

   A heading five per cent larger than the paragraph under it is not a
   heading. The two small display sizes are therefore floored at a fixed
   multiple of the reading size, so the step between levels can never close
   no matter how wide the window is. The larger display sizes already keep a
   ratio above 1.6 and are left as the design drew them.
   ========================================================================== */
.display.d-sm {
  font-size: max(clamp(1.25rem, 1.5vw, 1.45rem), calc(var(--sg-read) * 1.28));
}
.display.d-md {
  font-size: max(clamp(1.42rem, 2vw, 1.75rem), calc(var(--sg-read) * 1.46));
}
@media (min-width: 761px) and (max-width: 1199.98px) {
  /* d-md carries an interpolated curve through the tablet band, so its floor
     has to be applied against that curve rather than the base clamp. d-sm has
     no tablet curve of its own and is already handled by the rule above. */
  .display.d-md { font-size: max(calc(49.75px - 2.1455vw), calc(var(--sg-read) * 1.46)); }
}

/* The dot between the footer links is punctuation, not content. It is marked
   aria-hidden so a screen reader does not announce "middot" between every
   link, and lifted from 0.2 to 0.42 so a sighted reader can actually see the
   separation it is there to provide. */
.foot__legal > span[aria-hidden]:not(.mono) {
  color: rgba(var(--foot-ink), 0.42);
}

/* ==========================================================================
   The archway draw-in, on a phone

   Scrolling into the Sacred spaces block on a phone stuttered. Measured at a
   397px viewport the cause is exact: the drawing's paths animate
   stroke-dashoffset, and almost all of them animate it at once.

   Every other property these drawings use -- opacity, transform -- is handed
   to the compositor and costs the page nothing. stroke-dashoffset cannot be:
   the path is re-tessellated and re-rasterised on the main thread every frame.
   Thirty-three of those together is past what a phone holds at 60fps.

   The answer is not to remove the drawing. It is to stop drawing every line
   simultaneously, which was never what the effect was imitating anyway. A hand
   draws one line at a time. Each stroke is quicker and the sequence is spread,
   so the same drawing arrives the same way with about a fifth as many paths in
   flight:

       concurrency ~= paths x duration / span
       before  33 x 2.40 / 5.10 ~= 15 average, close to 30 at the peak
       after   33 x 0.70 / 4.75 ~= 5

   Tablets and desktops keep the original tempo; they were measured and showed
   no main-thread contention at all.
   ========================================================================== */
@media (max-width: 760px) {
  .art .draw-on,
  .phead__art .draw-on,
  .hero__art .draw-on,
  .panel__art .draw-on {
    animation-duration: .7s;
    animation-delay: calc(var(--delay, 0s) * 1.5);
    animation-timing-function: cubic-bezier(.33, 0, .2, 1);
  }
}

/* The plate's slow breath was bounded to three passes in an earlier pass of
   this work, on the reasoning that an infinite animation is a permanent cost.
   The reasoning was wrong and the measurement says so: it animates opacity on
   an element already promoted to its own layer by will-change, so the
   compositor runs it without touching the page, and it was never part of the
   stutter. The cost was the concurrent stroke-dashoffset drawing, which is
   handled above. The breath is left as it was designed. */

/* ==========================================================================
   One footer order, every screen

   The Workshop block -- status, hours, city -- is authored last and was
   lifted to the top of the phone footer with order:-1. That put two
   sequences in one page: a sighted phone reader met it first, and anything
   following the document -- a screen reader, reader mode, a crawler
   summarising the page -- met it last, after the three link columns.

   Measured on index.html, only the phone disagreed:

       >= 901px    brand  col col col  status      document order
       621-900px   brand / col col col / status    document order
       <= 620px    brand  STATUS  col col col      reordered

   So the mismatch was these two declarations. Removing them costs 187px --
   the status block now sits under three folded column headers rather than
   over them, still well inside the footer, and the footer's height is
   unchanged. The brand rule was already doing nothing: the brand is the
   first child in the document as well.
   ========================================================================== */
@media (max-width: 620px) {
  .foot__brand { order: 0; }
  .foot__col--status { order: 0; }
}

/* ==========================================================================
   A focus ring for the constellation hubs

   The five hubs are SVG links. Their own rule switched the global focus ring
   off and signalled focus by tinting the label, using the colour hover
   already used. Measured on the page:

       label at rest       #F0EEE6    15.87:1 on the #141413 ground
       label on focus      #C2AA7E     8.20:1 on the same ground
       rest versus focus   1.93:1

   1.93:1 is under the 3:1 a non-text state indicator needs, and since hover
   paints the same colour, focus and hover were indistinguishable as well.

   A ring is geometry, not colour: it is drawn or it is not. That separates
   focus from hover for every reader, including one who cannot resolve the
   tint. The tint is kept -- the ring is added over it.
   ========================================================================== */
.cn__hub:focus-visible {
  outline: 2px solid #C2AA7E;
  outline-offset: 6px;
  border-radius: 3px;
}

/* ==========================================================================
   Decorative drawings as stacked layers

   An animation on anything *inside* an <svg> dirties the layer the whole
   <svg> is painted into, and everything under the dirty area is redrawn --
   not just the moving element. The archway on collections#sacred is 633
   primitives; its draw-on intro runs 32 strokes across the full height of
   the arch for about five seconds, exactly while a phone is scrolling into
   the section, and each frame repainted the dots and lattice behind them.
   Measured in Chrome at phone size (375 x 812 @3x, CPU slowed 4x), that
   intro cost 803 ms of main-thread paint and 79 ms of raster per 6 s.

   The drawings are now split in paint order into sibling <svg> layers
   inside .sg-art-stack. Each keeps the original viewBox, so geometry is
   untouched (measured: 0.01 px worst case across every drawing at 375, 820
   and 1440 wide), and the stack is one group, so the plate's breath still
   applies to the whole drawing. What changes is what repaints:

     static dots and lattice   rasterised once, then only composited
     draw-on intro strokes     their own layer, repainted only while drawing
     glow, rings, tail, head   lifted to the layer's root, where every
     and arm loops             engine runs transform/opacity on the
                               compositor with no paint at all

   Same intro after the split: 499 ms paint, 23 ms raster. On phones the
   peacock's head and tail sway without end; with both lifted, its paint
   while idle in view fell from 1817 ms to 612 ms per 8 s, the grove's from
   1917 ms to 440 ms, and frames over 25 ms from 24 to 5 and 27 to 4.

   A lifted loop turns about the same point it did inside the drawing. An
   SVG child's transform-origin resolves in its own user space -- `center`
   is the centre of a viewBox-sized box anchored at (0,0), not the centre of
   the shape -- and that point is carried to the layer as a percentage of
   the viewBox, which the layer's box matches because its height follows
   the viewBox ratio. Timings, easing, iteration counts and the in-view gate
   are those of the original classes.
   ========================================================================== */
.sg-art-stack {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  animation: plate-breathe 7s ease-in-out infinite;
  will-change: opacity;
}
.art-gate > .sg-art-stack { animation-play-state: paused; }
.art-gate.is-in > .sg-art-stack { animation-play-state: running; }
.sg-art-stack > svg { display: block; position: relative; animation: none; }
.sg-art-stack > svg + svg { position: absolute; inset: 0; }
.sg-art-stack > .sg-h { will-change: transform, opacity; }
.sg-art-stack > .sg-h--breathe-glow   { animation: breathe-glow 8s ease-in-out 3 both; }
.sg-art-stack > .sg-h--breathe-center { animation: breathe-center 6s ease-in-out 3 both; }
.sg-art-stack > .sg-h--rotate-ccw-45  { animation: rotate-ccw 45s linear 1 both; }
.sg-art-stack > .sg-h--rotate-ccw-50  { animation: rotate-ccw 50s linear 1 both; }
.sg-art-stack > .sg-h--rotate-ccw-75  { animation: rotate-ccw 75s linear 1 both; }
.sg-art-stack > .sg-h--rotate-cw-60   { animation: rotate-cw 60s linear 1 both; }
.sg-art-stack > .sg-h--rotate-cw-90   { animation: rotate-cw 90s linear 1 both; }
.sg-art-stack > .sg-h--arm-carve      { animation: arm-carve 1.5s ease-in-out 12 both; }
.sg-art-stack > .sg-h--tail-drift     { animation: tail-drift 9s ease-in-out 3 both; }
.sg-art-stack > .sg-h--tail-sway      { animation: tail-sway 8s ease-in-out 3 both; }
.sg-art-stack > .sg-h--head-bob       { animation: sg-head-bob 6s ease-in-out 4 both; }
@media (max-width: 760px) {
  .sg-art-stack > .sg-h--tail-drift,
  .sg-art-stack > .sg-h--tail-sway { animation: drift-phone 7.7s ease-in-out infinite; }
  .sg-art-stack > .sg-h--head-bob  { animation: sg-bob-phone 5s ease-in-out infinite; }
}
/* head-bob and bob-phone nod by 2 and 3 px of the drawing's own units; on
   the lifted layer the same distance is that many units of its height,
   given per drawing as --sg-u. */
@keyframes sg-head-bob {
  0%, 100% { transform: rotate(-1.5deg) translateY(0); }
  50%      { transform: rotate(1deg) translateY(calc(var(--sg-u) * -2)); }
}
@keyframes sg-bob-phone {
  0%, 100% { transform: rotate(-3deg) translateY(0); }
  50%      { transform: rotate(2deg) translateY(calc(var(--sg-u) * -3)); }
}
.art-gate .sg-art-stack > .sg-h--tail-drift,
.art-gate .sg-art-stack > .sg-h--tail-sway,
.art-gate .sg-art-stack > .sg-h--head-bob { animation-play-state: paused; }
.art-gate.is-in .sg-art-stack > .sg-h--tail-drift,
.art-gate.is-in .sg-art-stack > .sg-h--tail-sway,
.art-gate.is-in .sg-art-stack > .sg-h--head-bob { animation-play-state: running; }

/* Two drawings are letterboxed: the peacock grove (a 3:2 box, a band on
   phones, around a 22:9 viewBox) and the stone artisan (3:2 around 22:15).
   A percentage of such a box is a point of the drawing only at its centre,
   and both have loops that turn about other points. With container units
   the lifted layer is sized to the drawing's own rectangle -- the `meet`
   fit, centred -- and the percentages hold again. Without them the loops
   stay inside the drawing, exactly as before. */
@supports (width: 1cqh) {
  .sg-art-stack--fit { container-type: size; }
  .sg-art-stack--fit > .sg-hf {
    inset: 0;
    margin: auto;
    width: min(100cqw, 100cqh * var(--sg-vw) / var(--sg-vh)) !important;
    height: min(100cqh, 100cqw * var(--sg-vh) / var(--sg-vw)) !important;
    will-change: transform, opacity;
  }
  .sg-art-stack--fit .sg-hsrc { animation: none !important; }
  .sg-art-stack--fit > .sg-hf--breathe-glow { animation: breathe-glow 8s ease-in-out 3 both; }
  .sg-art-stack--fit > .sg-hf--arm-carve    { animation: arm-carve 1.5s ease-in-out 12 both; }
  .sg-art-stack--fit > .sg-hf--tail-drift   { animation: tail-drift 9s ease-in-out 3 both; }
  .sg-art-stack--fit > .sg-hf--tail-sway    { animation: tail-sway 8s ease-in-out 3 both; }
  .sg-art-stack--fit > .sg-hf--head-bob     { animation: sg-head-bob 6s ease-in-out 4 both; }
  @media (max-width: 760px) {
    .sg-art-stack--fit > .sg-hf--tail-drift,
    .sg-art-stack--fit > .sg-hf--tail-sway { animation: drift-phone 7.7s ease-in-out infinite; }
    .sg-art-stack--fit > .sg-hf--head-bob  { animation: sg-bob-phone 5s ease-in-out infinite; }
  }
  .art-gate .sg-art-stack--fit > .sg-hf--tail-drift,
  .art-gate .sg-art-stack--fit > .sg-hf--tail-sway,
  .art-gate .sg-art-stack--fit > .sg-hf--head-bob { animation-play-state: paused; }
  .art-gate.is-in .sg-art-stack--fit > .sg-hf--tail-drift,
  .art-gate.is-in .sg-art-stack--fit > .sg-hf--tail-sway,
  .art-gate.is-in .sg-art-stack--fit > .sg-hf--head-bob { animation-play-state: running; }
}
