/* satimi.sook — the shop's own stylesheet.
   The design, the palette and the type scale are the owner's approved master
   (source/production-master/styles.css), kept deliberately intact. What
   changed underneath it: real <figure>/<figcaption> instead of captions faked
   with content:attr(), <picture> instead of <img>, a Thai webface instead of
   whatever the phone happened to own, and focus states. */

/* Thai. unicode-range is doing the real work here: a visitor on the English
   page never downloads these, and on the Thai page the browser reaches for
   them only for the glyphs Georgia and Arial cannot draw. That is also why the
   font stacks below can list Georgia FIRST and still typeset Thai correctly —
   per-glyph fallback, not per-page. */
@font-face{
  font-family:"Noto Sans Thai";font-style:normal;font-weight:400;font-display:swap;
  src:url(fonts/noto-sans-thai-400.woff2) format("woff2");
  unicode-range:U+0E01-0E5B,U+200C-200D,U+25CC;
}
@font-face{
  font-family:"Noto Sans Thai";font-style:normal;font-weight:700;font-display:swap;
  src:url(fonts/noto-sans-thai-700.woff2) format("woff2");
  unicode-range:U+0E01-0E5B,U+200C-200D,U+25CC;
}

:root{
  --cream:#f3ede2;--paper:#fbf7ef;--ink:#273232;--blue:#7897a5;
  --blue2:#587783;--yellow:#d8bd63;--brown:#654a3a;--muted:#69736f;
  --line:rgba(39,50,50,.14);
  --sans:Arial,Helvetica,"Noto Sans Thai",sans-serif;
  --serif:Georgia,"Noto Sans Thai",serif;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;background:var(--cream);color:var(--ink);font-family:var(--sans);line-height:1.5}
a{color:inherit;text-decoration:none}
ul{list-style:none;margin:0;padding:0}
picture{display:block;width:100%;height:100%}
img{width:100%;height:100%;display:block;object-fit:cover}
.shell{width:min(1240px,calc(100% - 42px));margin:auto}

/* Keyboard users get a visible ring; nobody else sees one. */
a:focus-visible,.button:focus-visible{outline:2px solid var(--ink);outline-offset:3px;border-radius:4px}
.scoops a:focus-visible,.product-copy a:focus-visible,footer a:focus-visible{outline-color:#fff}
.skip{position:absolute;left:-9999px;z-index:60}
.skip:focus{left:12px;top:12px;padding:11px 16px;background:var(--ink);color:var(--cream);border-radius:8px}

nav{position:fixed;z-index:30;inset:0 0 auto;background:rgba(243,237,226,.89);backdrop-filter:blur(14px);border-bottom:1px solid var(--line)}
.nav{height:70px;display:flex;align-items:center;justify-content:space-between}
.brand{font-size:22px;font-weight:800;letter-spacing:-1px}
.brand span{font-weight:400;color:var(--blue2)}
.nav-right{display:flex;align-items:center}
.links{display:flex;gap:25px;font-size:11px;text-transform:uppercase;letter-spacing:.11em}
.lang{display:flex;gap:7px;align-items:center;margin-left:18px;font-size:11px;letter-spacing:.1em}
.lang a{padding:5px 2px;color:var(--muted)}
.lang a.active{color:var(--ink);font-weight:800;border-bottom:1px solid var(--ink)}

.hero{padding:88px 0 22px;min-height:100svh}
.hero-photo{height:calc(100svh - 110px);min-height:600px;border-radius:24px;overflow:hidden;position:relative}
.hero-photo img{filter:saturate(.82) brightness(.91)}
.shade{position:absolute;inset:0;background:linear-gradient(90deg,rgba(23,35,32,.58),rgba(23,35,32,.13) 60%,rgba(23,35,32,.05))}
.hero-copy{position:absolute;left:clamp(24px,6vw,80px);bottom:clamp(34px,8vh,88px);color:#fff;max-width:780px}
.eyebrow{font-size:11px;letter-spacing:.18em;text-transform:uppercase;font-weight:700;margin:0}
h1,h2,h3{font-family:var(--serif);font-weight:400}
.hero h1{font-size:clamp(50px,7.2vw,102px);line-height:.9;letter-spacing:-.055em;margin:16px 0 24px}
.hero h1 em{font-style:normal;color:#e5cf82}
.hero-sub{font-size:18px;max-width:520px;margin:0;color:rgba(255,255,255,.9)}

.manifesto{padding:150px 0;background:var(--paper);border-bottom:1px solid var(--line)}
.manifesto p{font-family:var(--serif);font-size:clamp(30px,4.6vw,60px);line-height:1.1;letter-spacing:-.035em;max-width:1100px;margin:0}
.manifesto em{font-style:normal;color:var(--blue2)}

/* 120-160px of vertical padding per section, from the brief. Breathing
   space is treated there as part of the brand, not as slack to be
   reclaimed. */
section.block{padding:132px 0}
.head{display:grid;grid-template-columns:.8fr 1.2fr;gap:40px;margin-bottom:64px}
.kicker{font-size:12px;letter-spacing:.17em;text-transform:uppercase;color:var(--blue2);font-weight:700;margin:0}
h2{font-size:clamp(38px,5.2vw,70px);line-height:1;letter-spacing:-.045em;margin:8px 0 0}
h2 em{font-style:normal}
.desc{max-width:520px;color:var(--muted);font-size:17px;align-self:end}

.identity{display:grid;grid-template-columns:1.35fr .65fr;gap:16px}
.photo{overflow:hidden;border-radius:22px;position:relative;margin:0}
.photo figcaption{position:absolute;left:18px;bottom:16px;color:#fff;text-transform:uppercase;letter-spacing:.13em;font-size:10px;text-shadow:0 1px 6px #000}
.identity .wide,.identity .door{height:590px}
.identity img{filter:saturate(.88)}

.product-emotion{background:#1f2625;color:#fff}
.product-grid{display:grid;grid-template-columns:1.18fr .82fr;min-height:760px}
.product-image{min-height:760px}
.product-image img{filter:saturate(.82) brightness(.9)}
.product-copy{padding:70px clamp(30px,6vw,80px);display:flex;flex-direction:column;justify-content:center}
.product-copy .kicker{color:#dfc66f}
.product-copy h2{font-size:clamp(42px,5.5vw,76px)}
.product-copy h2 em{color:#dfc66f}
.product-copy p{max-width:410px;color:#c9cfca;font-size:16px}
.tiny{margin:38px 0 0;font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:#aeb8b2}

.scoops{background:var(--blue2);color:#f8f2e8}
.scoops .kicker{color:#ead477}
.scoops .desc{color:#e4e8e3}
/* Three photographs, not three cards. The brief asks for real flavour
   photography at roughly 4:5 and says in as many words not to use tiny
   three-column cards, so these are pictures first with the name under them,
   and on a phone they stack: image, flavour, one line. The colour discs that
   stood here were the only thing on the page standing in for a photograph. */
.flavours{display:grid;grid-template-columns:repeat(3,1fr);gap:32px}
.flavour{display:flex;flex-direction:column;align-items:flex-start}
.flavour figure{margin:0 0 20px;width:100%;aspect-ratio:4/5;overflow:hidden;border-radius:20px}
.flavour figure img{filter:saturate(.9)}
.flavour strong{font-family:var(--serif);font-size:clamp(24px,2.8vw,32px);font-weight:400;line-height:1.1}
.flavour span{opacity:.75;margin-top:8px;font-size:15px}
.note{font-size:12px;opacity:.7;margin:23px 0 0}

.cacao-grid{display:grid;grid-template-columns:.92fr 1.08fr;gap:16px}
.cacao-copy{min-height:620px;border-radius:24px;background:var(--brown);color:#fff6ea;padding:42px;display:flex;flex-direction:column;justify-content:space-between}
.cacao-copy .kicker{color:#e4c967}
.cacao-copy h2{font-size:clamp(38px,4.8vw,66px)}
.cacao-body{max-width:430px;opacity:.82;margin-top:24px}
.route li{border-top:1px solid rgba(255,255,255,.24);padding:13px 0;display:grid;grid-template-columns:38px 1fr;font-size:14px}
.route span{opacity:.8}
.cacao-img{height:620px;border-radius:24px;overflow:hidden}
.cacao-img img{filter:saturate(.85)}

.details-sec{background:var(--paper)}
/* An irregular composition, not a grid of three: one lifestyle photograph
   leading, two details beside it. The brief asks for exactly that and warns
   against an Instagram-style grid. The arch top stays; it is the shape this
   page already speaks in. */
.details-grid{display:grid;grid-template-columns:1.45fr 1fr;grid-template-rows:auto auto;gap:18px}
.details-grid .photo{border-radius:150px 150px 20px 20px;height:301px}
.details-grid .lead{grid-row:1 / span 2;height:620px}
.details-grid img{filter:saturate(.84)}

.play-grid{display:grid;grid-template-columns:1fr 1fr;gap:45px;align-items:center}
.play-img{height:500px;border-radius:24px;overflow:hidden}
.play-img img{filter:saturate(.84)}
.button{display:inline-flex;padding:13px 20px;border:1px solid currentColor;border-radius:999px;font-weight:700;margin-top:22px;transition:transform .2s}
.button:hover{transform:translateY(-1px)}
.button.disabled{opacity:.55;cursor:not-allowed}

.visit{background:var(--yellow)}
.visit-grid{display:grid;grid-template-columns:1fr 1fr;gap:60px}
.visit-info{align-self:end}
.launch-note{font-size:11px;margin:12px 0 0;opacity:.62}
.detail{padding:15px 0;border-top:1px solid rgba(39,50,50,.35)}
/* The directions button stands in the middle of the detail list, and a
   .detail draws its rule on its own top edge — so without this the next
   rule is ruled straight across the button's bottom. */
.visit-info .button{margin-bottom:28px}
.detail small{display:block;font-size:10px;letter-spacing:.14em;text-transform:uppercase;margin-bottom:5px}
.ig{display:block;margin-top:18px;font-weight:700;text-decoration:underline;text-underline-offset:4px}

footer{background:var(--ink);color:#f7f0e5;padding:65px 0}
.footer{display:flex;justify-content:space-between;gap:30px;align-items:end}
.footer strong{font-family:var(--serif);font-size:clamp(40px,6vw,76px);line-height:.92;font-weight:400}
.footer small{opacity:.65}

/* Thai sets taller and needs more room between lines than Georgia does at the
   same nominal size, so every display size steps down and every leading opens
   up. Straight from the owner's master, which had already solved this. */
html[lang="th"] body{line-height:1.62}
html[lang="th"] .hero h1{font-size:clamp(42px,6vw,82px);line-height:1.15;letter-spacing:-.025em}
html[lang="th"] h2{font-size:clamp(34px,4.6vw,60px);line-height:1.22;letter-spacing:-.02em}
html[lang="th"] .manifesto p{font-size:clamp(27px,4vw,51px);line-height:1.32;letter-spacing:-.015em}
html[lang="th"] .product-copy h2{font-size:clamp(36px,4.8vw,64px);line-height:1.2}
html[lang="th"] .footer strong{line-height:1.25}

@media(max-width:820px){
  .shell{width:calc(100% - 44px)}
  .links{display:none}
  .hero{padding-top:84px}
  .hero-photo{height:calc(100svh - 100px);min-height:570px;border-radius:18px}
  .hero-photo img{object-position:54% center}
  .shade{background:linear-gradient(0deg,rgba(20,31,28,.68),rgba(20,31,28,.06) 70%)}
  .hero-copy{left:24px;right:20px;bottom:38px}
  /* The brief is explicit: hero 38-46px, section headings 30-38px, body
     16-18px, and avoid oversized typography. What was here ran to 70px
     and 52px, which is desktop type shrunk rather than a phone design. */
  .hero h1{font-size:clamp(38px,10.5vw,46px);line-height:1.02}
  .hero-sub{font-size:16px}
  .manifesto{padding:90px 0}
  .manifesto p{font-size:clamp(26px,7.4vw,34px);line-height:1.2}
  .head,.identity,.cacao-grid,.play-grid,.visit-grid,.product-grid{grid-template-columns:1fr}
  section.block{padding:80px 0}
  h2{font-size:clamp(30px,8.4vw,38px)}
  .desc{font-size:16px;line-height:1.6}
  .identity .wide,.identity .door,.cacao-img{height:430px}
  .product-grid,.product-image{min-height:0}
  .product-image{height:62vh;min-height:450px}
  .product-copy{padding:55px 28px 70px}
  .product-copy h2{font-size:clamp(30px,8.8vw,40px)}
  .details-grid .photo{height:360px}
  .flavours{grid-template-columns:1fr;gap:44px}
  .details-grid{grid-template-columns:1fr}
  .details-grid .lead{grid-row:auto;height:440px}
  .cacao-copy{min-height:540px;padding:28px}
  .footer{display:block}
  .footer small{display:block;margin-top:28px}

  html[lang="th"] .hero h1{font-size:clamp(32px,8.8vw,40px);line-height:1.28}
  html[lang="th"] h2{font-size:clamp(27px,7.4vw,34px);line-height:1.32}
  html[lang="th"] .manifesto p{font-size:clamp(22px,6.2vw,29px);line-height:1.45}
  html[lang="th"] .product-copy h2{font-size:clamp(32px,9vw,46px);line-height:1.24}
}

@media(prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *{transition-duration:.01ms!important;animation-duration:.01ms!important}
}

/* The privacy policy — one bilingual document, borrowing this site's
   typography and, importantly, its Thai webface. A measured column: legal
   prose read on a phone is the one thing here that is pure reading. */
.doc{padding:118px 0 90px}
.doc .shell{max-width:660px}
.doc h1{font-size:clamp(34px,6vw,50px);margin:0 0 8px}
.doc h2{font-size:clamp(21px,3.4vw,27px);margin:40px 0 10px}
.doc p{margin:0 0 14px;font-size:16px}
.doc a{text-decoration:underline;text-underline-offset:3px}
.doc-sub{color:var(--muted)}
.doc-stamp{color:var(--muted);font-size:14px}
.doc hr{border:0;border-top:1px solid var(--line);margin:56px 0 44px}
.doc-back{display:inline-block;margin-top:44px;font-weight:700;color:var(--blue2)}
/* Thai needs the leading; and its headings take the sans face, because Georgia
   has no Thai glyphs and the serif stack would fall through to Noto anyway. */
.doc [lang="th"]{line-height:1.9}
.doc [lang="th"] h1,.doc [lang="th"] h2{font-family:var(--sans);font-weight:700;line-height:1.45}

.footer-end{display:flex;flex-direction:column;align-items:flex-end;gap:10px}
.footer-link{font-size:13px;opacity:.75;text-decoration:underline;text-underline-offset:3px}
@media(max-width:820px){
  .footer-end{align-items:flex-start;margin-top:28px}
  .doc{padding:100px 0 70px}
}

/* The game section. Its own art, not a photograph — so unlike every other
   picture on this page these have transparent backgrounds and sit ON the
   paper rather than inside a cropped box. */
.play .play-grid{align-items:center}
.play-art{border-radius:24px;overflow:hidden;background:#2e2118}
.play-art img{width:100%;height:auto;display:block}

.play-strip{
  display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;
  gap:30px 50px;margin-top:64px;padding-top:38px;border-top:1px solid var(--line);
}
.play-tubs,.play-cast{display:flex;align-items:flex-end;gap:22px}
/* The pans are square and read as a row of choices; the people are standing on
   the same line, which is what makes the strip a scene rather than a sprite
   sheet. */
.play-tubs img{width:104px;height:auto;display:block}
.play-cast{gap:8px}
.play-cast img{width:auto;height:172px;display:block}

@media(max-width:820px){
  .play-art{border-radius:18px}
  .play-strip{margin-top:44px;gap:26px 30px;justify-content:flex-start}
  .play-tubs img{width:78px}
  .play-cast img{height:132px}
}

/* BUILT WITH NATURE.
   The brief asks for calm and architectural, not an image gallery — so the
   restraint here is deliberate: one dominant photograph, two small details
   that do NOT stretch to fill the row, and a lot of paper left empty.

   DOM order is the mobile order; desktop is composed entirely with
   grid-template-areas, so nothing is duplicated and the reading order stays
   the one a screen reader should hear. */
.nature{background:var(--paper)}
.nature-grid{display:grid;gap:0 56px;
  /* ~61% of the row for the photograph, inside the 60-65% the brief asks for. */
  grid-template-columns:1fr 1.75fr;
  /* The empty fifth row is load-bearing. The photograph is a tall portrait, so
     without somewhere to put the slack the four text rows stretch to match its
     height and the label, headline and story drift apart down the column. All
     the spare height goes into that row instead, and the text stacks where it
     should. */
  grid-template-rows:auto auto auto auto 1fr auto;
  grid-template-areas:
    "label   hero"
    "head    hero"
    "story   hero"
    "credit  hero"
    ".       hero"
    "details closing";
  align-items:start}
.nature-label{grid-area:label}
.nature-head{grid-area:head;margin-bottom:26px}
.nature-hero{grid-area:hero;margin:0;border-radius:24px;overflow:hidden;align-self:stretch}
/* Her one explicit instruction about this photograph: keep the vertical rhythm
   of the columns, and avoid aggressive cropping. So it is not cover-cropped to
   a fixed box like the photographs elsewhere on the page — it keeps its own
   proportions and the column beside it grows to match. */
.nature-hero img{width:100%;height:auto;object-fit:contain}
.nature-story{grid-area:story;color:var(--muted);font-size:17px;max-width:460px}
.nature-story p{margin:0 0 14px}
.nature-credit{grid-area:credit;margin:16px 0 0;font-size:14px;color:var(--muted);max-width:460px}
.nature-details{grid-area:details;display:flex;gap:26px;margin-top:64px;max-width:640px}
.nature-details figure{margin:0;flex:1}
.nature-details .photo,.nature-details img{border-radius:16px}
.nature-details figcaption{margin-top:12px;font-size:13px;color:var(--muted);line-height:1.5}
.nature-details figcaption b{display:block;font-weight:700;color:var(--ink);
  font-size:10px;letter-spacing:.14em;text-transform:uppercase;margin-bottom:5px}
.nature-closing{grid-area:closing;align-self:end;justify-self:end;text-align:right;
  font-family:var(--serif);font-size:clamp(22px,2.4vw,32px);line-height:1.25;
  color:var(--blue2);margin:64px 0 0;max-width:300px}

@media(max-width:820px){
  /* One column, in her stated order: label, headline, photo, story, the two
     details, credit, closing. */
  .nature-grid{grid-template-columns:1fr;gap:0;
    grid-template-areas:"label" "head" "hero" "story" "details" "credit" "closing"}
  .nature-hero{border-radius:18px}
  .nature-head{margin-bottom:20px}
  .nature-hero{margin:6px 0 26px}
  .nature-details{flex-direction:column;gap:30px;margin-top:34px;max-width:none}
  .nature-credit{margin-top:30px}
  .nature-closing{justify-self:start;text-align:left;margin-top:34px;max-width:none}
}

/* Her own channels, in the Visit block. Quiet: a shop's social links are a
   footnote to "come and say hi", not the point of it. */
.social{display:flex;flex-wrap:wrap;gap:18px;margin:20px 0 0}
.social a{font-weight:700;font-size:14px;text-decoration:underline;text-underline-offset:4px}
.visit .detail strong a{text-decoration:underline;text-underline-offset:3px}

/* Our story. Text only, between two photograph-heavy sections, so the pause is
   the point. It reuses .head's two-column rhythm rather than inventing a new
   one, because it is the same beat as every other section opener here. */
.story .head{margin-bottom:0}
.story-copy{max-width:520px;color:var(--muted);font-size:17px;align-self:end}
.story-copy p{margin:0 0 14px}
.story-copy p:last-child{margin-bottom:0}
/* THE BRAND MOMENT. The brief asks for this by name: give "From the Scoop to
   the Space" generous breathing room. So it gets a rule above it, a lot of
   air, and three sizes of voice rather than one line of caption. */
/* SATIMI | SATIMI.SOOK. One aspect ratio for both, because two photographs
   of different shapes read as two photographs and not as a before and after.
   The name sits UNDER each rather than in the site's usual overlay corner:
   here the label is the whole point, and 10px white over a photograph is too
   quiet to carry it. */
/* minmax(0,1fr), not 1fr: a 1fr track's automatic minimum is the item's
   MIN-CONTENT, and a photograph's min-content is its intrinsic width, so
   two big pictures push the tracks wider than the shell and the whole
   page scrolls sideways. It did, by 11px, before this. */
.lineage-pair{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:26px;margin:72px 0 0}
.lineage-pair figure{margin:0}
/* The ratio goes on the PICTURE, not the figure: a figure carrying both the
   box and the caption gives the caption's height away out of the photograph,
   and the two pictures stop matching, which is the one thing this pair has
   to do. These figures deliberately do not take .photo, whose caption is an
   overlay. */
.lineage-pair picture{display:block;aspect-ratio:3/2;overflow:hidden;border-radius:20px}
.lineage-pair figcaption{margin-top:14px;color:var(--muted);
  font-size:12px;letter-spacing:.2em;font-weight:700}
.lineage-pair img{filter:saturate(.88)}

.lineage{margin:104px 0 0;padding-top:44px;border-top:1px solid var(--line);
  display:flex;flex-direction:column;align-items:flex-start;gap:16px}
.lineage b{font-size:12px;letter-spacing:.2em;font-weight:700;color:var(--ink)}
.lineage em{font-style:normal;font-family:var(--serif);color:var(--blue2);
  font-size:clamp(28px,3.6vw,44px);line-height:1.1;letter-spacing:-.02em}
.lineage span{color:var(--muted);font-size:16px}

@media(max-width:820px){
  .story-copy{margin-top:20px}
  .story-lineage{margin-top:38px}
}

/* From cacao to cup. Three sentences and four words, which is all the brief
   allows: keep the process simple, no technical infographic. */
.cacao-process{margin-top:96px;padding-top:48px;border-top:1px solid var(--line)}
.cacao-process h3{font-family:var(--serif);font-weight:400;font-size:clamp(24px,2.8vw,34px);
  margin:0 0 20px;letter-spacing:-.02em}
.cacao-process p{max-width:560px;color:var(--muted);font-size:17px;margin:0 0 14px}
.cacao-close{color:var(--ink);font-family:var(--serif);font-size:clamp(18px,2vw,22px);
  margin-top:26px!important;max-width:560px}
/* One line, four steps, arrows between. A list rather than a diagram, so it
   wraps on a phone instead of scrolling sideways. */
.cacao-steps{display:flex;flex-wrap:wrap;align-items:center;gap:6px 0;
  margin:40px 0 0;padding:0;list-style:none}
.cacao-steps li{display:flex;align-items:center;font-size:14px;letter-spacing:.04em;color:var(--ink)}
.cacao-steps li+li::before{content:"→";margin:0 16px;color:var(--blue2);opacity:.75}

/* Instagram, as the brief words it, at the foot of the Visit block. Subtle,
   one line, no feed and no grid. */
.social{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 16px;margin:26px 0 0;font-size:15px}
.social:empty{display:none}
.social span{color:var(--muted)}
.social a{font-weight:700;text-decoration:underline;text-underline-offset:4px}
.social-alt{font-weight:400!important;opacity:.7;font-size:14px}

@media(max-width:820px){
  .lineage-pair{grid-template-columns:minmax(0,1fr);gap:28px;margin-top:44px}
  .lineage{margin-top:66px;padding-top:32px;gap:12px}
  .cacao-process{margin-top:62px;padding-top:34px}
  .cacao-steps{margin-top:28px}
  .cacao-steps li+li::before{margin:0 10px}
}

/* Gentle reveal, on scroll, with no JavaScript.
   The brief asks for a slow island rhythm: a soft fade and rise as a section
   arrives, and explicitly not parallax, bouncing or constant movement. This is
   a scroll-driven CSS animation, so it costs no script on a site whose only
   script is the analytics tag.

   Three guards, because content that never fades IN is the one failure worth
   fearing more than no animation at all. It is behind @supports, so a browser
   without scroll-driven animations simply shows everything as it does today.
   It is behind prefers-reduced-motion. And the range starts at `entry 0%`, so
   an element that is already on screen when the page loads is past the start
   of its own range and renders at the finished state. */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .block .head,
    .identity figure,
    .flavour,
    .details-grid figure,
    .nature-hero,
    .nature-details figure,
    .cacao-img,
    .cacao-process,
    .lineage,
    .play-art,
    .visit-info {
      animation: sook-rise linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 28%;
    }
  }
}
@keyframes sook-rise {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: none; }
}
