@font-face{font-family:Palmore;src:url(fonts/PalmoreSemibold.woff2) format('woff2');font-weight:600;font-display:swap}
@font-face{font-family:'Funkis A';src:url(fonts/FunkisA-Regular.woff2) format('woff2');font-weight:400;font-display:swap}
@font-face{font-family:'Funkis A';src:url(fonts/FunkisA-Bold.woff2) format('woff2');font-weight:700;font-display:swap}

:root{
  --red:#D51008; --red-dark:#BE1E2D;   /* Figma "Christmas Dark Red" — the bauble + footer ground */
  --green:#268047;
  --mint:#F3FEBA; --mint-light:#F6FFC5; --lime:#E0FF54; --lime-border:#C6EA28;
  --plate:#EAE6E1; --paper:url(assets/texture.webp);
}
*{box-sizing:border-box}
html{overflow-x:clip;scroll-behavior:smooth}
body{margin:0;max-width:100%;background:var(--mint-light);
  font-family:'Funkis A',system-ui,sans-serif;color:var(--green);-webkit-font-smoothing:antialiased}
img{display:block;max-width:100%}
a{color:inherit}
.display{font-family:Palmore,Georgia,serif;font-weight:600;text-transform:uppercase}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}

/* ---------- paper texture overlay ----------
   Same contract as the Christmas page: z-index:-1 puts the grain above the section's
   background colour but behind every child, and `isolation` is what lets a -1 pseudo
   land above its own parent's background instead of dropping behind it.
   The tile is 1520x1520 and NEVER scales on desktop — see the mobile override at the
   foot of this file. */
.paper{isolation:isolate}
.paper::after{content:"";position:absolute;inset:0;background-image:var(--paper);
  background-size:auto;background-repeat:repeat;background-position:center;
  mix-blend-mode:multiply;opacity:.6;pointer-events:none;z-index:-1}

/* ---------- nav (lifted from the Christmas bundle; behaviour mirrors grain.com.sg) ---------- */
.nav{background:var(--red);min-height:60px;display:flex;align-items:center;position:sticky;top:0;z-index:40}
.nav-in{width:100%;max-width:1176px;margin:0 auto;padding:0 24px;display:flex;align-items:center;gap:16px}
.nav-logo{width:80px;height:18px;flex:none}
.nav .nav-logo{padding:0}
.nav-logo img{width:100%;height:100%;object-fit:contain}
.nav-links{display:flex;align-items:center;gap:0;flex:1}
.nav-left,.nav-right{display:flex;align-items:center;gap:0}
.nav-right{margin-left:auto}
.nav a{color:var(--plate);font-family:inherit;font-size:14px;font-weight:700;text-decoration:none;white-space:nowrap;background:none;border:0;padding:0 12px;cursor:pointer;display:inline-flex;align-items:center;gap:6px}
.nav-burger{display:none;margin-left:auto;background:none;border:0;color:var(--plate);cursor:pointer;padding:4px}

/* ---------- button (Figma 13934:4686) ---------- */
.btn{display:inline-flex;align-items:center;justify-content:center;
  background:var(--lime);border:.5px solid var(--lime-border);border-radius:2px;
  padding:12px 24px;min-height:42px;color:var(--green);
  font-family:'Funkis A',system-ui,sans-serif;font-weight:700;font-size:15px;line-height:1.5;
  text-align:center;text-decoration:none;cursor:pointer;
  filter:drop-shadow(1px 1px 5px rgba(187,211,94,.3));transition:filter .15s ease}
.btn:hover{filter:drop-shadow(1px 2px 7px rgba(187,211,94,.55))}
.btn:focus-visible{outline:2px solid var(--green);outline-offset:2px}

/* ================= HERO — the pack counter (Figma 13934:996) =================
   The Figma frame is a fixed 1728x678 band with the stack floating in it. Padding is
   a FLOOR, not a fixed height: the band grows rather than clips when the copy runs
   long (the same call the Christmas page's fixed-height bands make). */
.hero{position:relative;overflow:hidden;padding:clamp(40px,8svh,102px) 24px clamp(48px,9svh,127px);background:var(--mint-light);
  /* The first fold: the hero fills the viewport under the sticky nav (60px here,
     66px under 1200), so its anise rail always sits on the fold line. vh first as
     the fallback; svh so iOS's collapsing toolbar can't push the rail off-screen.
     A floor, not a height — a short viewport lets the hero grow past the fold
     rather than clip the counter. The content centres in any spare height. */
  --nav-h:60px;min-height:calc(100vh - var(--nav-h));min-height:calc(100svh - var(--nav-h));
  display:flex;flex-direction:column;justify-content:center}
.hero-in{width:100%}
.hero-in{max-width:650px;margin:0 auto;display:flex;flex-direction:column;align-items:center}

.hero h1{margin:0;font-size:72px;line-height:.78;color:var(--red);text-align:center;letter-spacing:.01em}

/* ---- GRAIN x Food From The Heart lockup (Figma 13934:4690) ---- */
.lockup{display:flex;align-items:center;gap:24px;margin-top:40px}
.lockup-grain{width:81.43px;height:18px}
.lockup-x{width:16.97px;height:16.98px}
.lockup-ffth{width:146px;height:40px}

/* ---- goal · dial · caption ----
   The row is exactly as wide as the progress bar under it (650), and the three
   blocks keep their Figma widths and gutters: 152 + 85 + 161 + 96 + 156 = 650. */
.counter{display:flex;align-items:center;width:100%;margin-top:51px}

.goal{flex:none;width:152px;margin-right:85px;
  border:1px solid var(--green);border-radius:24px;padding:12px 24px;
  display:flex;flex-direction:column;align-items:flex-start;white-space:nowrap}
.goal-label{font-weight:700;font-size:15px;line-height:1.5}
.goal-row{display:flex;align-items:baseline;gap:6px;margin-top:1px}
.goal-num{font-family:Palmore,Georgia,serif;font-weight:600;font-size:36px;line-height:1}
.goal-unit{font-weight:400;font-size:15px;line-height:1.5}

/* The pointer's tip tucks under the bauble, so the two share one 161px box rather
   than sitting in a gap: pointer at x0, bauble at x15 (Figma 776 / 791). */
.dial{flex:none;position:relative;width:161px;height:144px;margin-right:96px}
/* Figma's Polygon FRAME is 33.25x30.31 but the vector inside fills only
   87.27% x 89.04% of it, flush left and vertically centred (inset 5.48% 12.73% 0).
   Sizing the <img> to the frame stretched the triangle ~15% wider, which drove its
   tip 18px into the bauble and the bauble ate half of it. The vector's own 29.02 x
   26.99 leaves the tip just touching the bauble's left point, as in the render. */
/* z-index:2 because in Figma the Polygon sits ABOVE the Cutout, so the triangle's
   tip laps ~13px ONTO the bauble rather than being swallowed by it. Painted under
   the bauble it loses a third of its length and reads as a stub. */
.dial-pointer{position:absolute;left:0;top:58.66px;width:29.02px;height:26.99px;z-index:2}
.dial-shape{position:absolute;left:15px;top:0;width:146px;height:144px;display:block}

/* The window IS the Figma cutout: the same bauble path, translated to a 0,0 origin
   so it can be a clip-path on a 146x144 box. Everything in the roll is clipped to
   it, which is what keeps the neighbour numbers off the paper. */
.dial-win{position:absolute;left:15px;top:0;width:146px;height:144px;
  clip-path:path("M112.222 21.312C95.066 4.724 74.096 -9.1 52.175 7.488C33.113 22.234 9.284 42.509 0.706 65.549C-3.106 82.137 9.285 95.039 19.77 107.941C55.988 145.727 75.05 161.394 117.939 117.158L118.894 116.236C161.784 75.686 149.393 57.254 112.222 21.312Z");
  -webkit-clip-path:path("M112.222 21.312C95.066 4.724 74.096 -9.1 52.175 7.488C33.113 22.234 9.284 42.509 0.706 65.549C-3.106 82.137 9.285 95.039 19.77 107.941C55.988 145.727 75.05 161.394 117.939 117.158L118.894 116.236C161.784 75.686 149.393 57.254 112.222 21.312Z")}
.dial-roll{position:absolute;left:0;top:0;width:146px;height:144px}
.dial-face{position:absolute;left:0;top:0;width:146px;height:144px;margin:0;
  display:grid;place-items:center;
  font-size:72px;line-height:1;color:var(--mint-light);
  font-variant-numeric:tabular-nums;letter-spacing:.01em;
  will-change:transform,opacity}
/* The live figure is announced from a visually-hidden node, because the faces are
   recycled by the roll and would otherwise read every number it passes through. */
.dial-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;
  overflow:hidden;white-space:nowrap;clip-path:inset(50%)}

/* ---- no-JS resting state ----
   63px is Figma's pitch between the three stacked numbers. The gradient is measured
   off the render: the neighbour reads at ~0.62 where it meets the bauble's edge and
   is gone 28px in, so it never competes with the live figure. Once app.js takes the
   dial over (html.js-roll) the drum sets transform and opacity per frame instead,
   and the masks come off — a mask anchored to a MOVING face sweeps with it and eats
   the number just as it lands in the middle. */
html:not(.js-roll) .dial-face.is-prev{transform:translateY(-63px);
  -webkit-mask-image:linear-gradient(to bottom,rgba(0,0,0,.62) 0 63px,transparent 91px);
          mask-image:linear-gradient(to bottom,rgba(0,0,0,.62) 0 63px,transparent 91px)}
/* `to top` measures from the BOTTOM edge, so the two ramps are the same 63/91 —
   each neighbour is strongest where it meets the bauble and gone 28px inside. */
html:not(.js-roll) .dial-face.is-next{transform:translateY(63px);
  -webkit-mask-image:linear-gradient(to top,rgba(0,0,0,.62) 0 63px,transparent 91px);
          mask-image:linear-gradient(to top,rgba(0,0,0,.62) 0 63px,transparent 91px)}

.counter-cap{flex:none;width:156px;margin:0;font-weight:700;font-size:15px;line-height:1.5}

/* ---- campaign progress (Figma 13937:5932) ---- */
.progress{width:100%;margin-top:50px;display:flex;flex-direction:column;align-items:center}
.bar{position:relative;width:100%;height:5px;border-radius:2.5px;
  background:rgba(38,128,71,.5)}
.bar-fill{position:absolute;left:0;top:0;height:100%;width:0;border-radius:2.5px;background:var(--green);
  transition:width 1.6s cubic-bezier(.65,.01,.05,.99)}
.bar-ends{display:flex;justify-content:space-between;width:100%;margin-top:22px;
  font-size:15px;line-height:1.5}
.updated{display:flex;align-items:center;gap:9px;margin:24px 0 0;font-size:15px;line-height:1.5}
/* Explainer under the bar. Same 15/1.5 body and one-line paragraph gap as .story-copy. */
.progress-note{max-width:520px;display:flex;flex-direction:column;gap:22.5px;text-align:center}
.progress-note p{margin:0;font-size:15px;line-height:1.5}

/* Re-check the figure without reloading. The page already fetches on load, so
   this is the "I'm watching it climb" affordance, not the only way to get fresh
   data. Sits on the baseline of the 15/1.5 label — hence the -1px nudge. */
.updated .refresh{display:inline-flex;align-items:center;justify-content:center;flex:none;
  /* Leads the line where the bullet used to. The 22px hit area is deliberately
     bigger than the 13px glyph, so the negative margins pull the BOX back and let
     the glyph sit on the same optical gap the 6px dot had. */
  width:22px;height:22px;margin:-1px -4px 0 -5px;padding:0;border:0;border-radius:50%;
  background:none;color:var(--green);cursor:pointer;-webkit-appearance:none;appearance:none;
  transition:background-color .15s ease,opacity .15s ease}
.updated .refresh svg{width:13px;height:13px;display:block}
.updated .refresh:hover{background:rgba(38,128,71,.12)}
.updated .refresh:focus-visible{outline:2px solid var(--green);outline-offset:2px}
.updated .refresh[disabled]{cursor:default;opacity:.55}
.updated .refresh.is-busy svg{animation:refreshSpin .7s linear infinite;transform-origin:50% 50%}
@keyframes refreshSpin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}
@media (prefers-reduced-motion: reduce){
  .updated .refresh.is-busy svg{animation:none}
}
/* The status text is for screen readers only — sighted users see the dial move. */
.refresh-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}

/* ================= STORY (Figma 13934:1023) ================= */
.story{position:relative;overflow:hidden;padding:112px 24px 46px;background:var(--mint-light)}
.story-in{max-width:500px;margin:0 auto;display:flex;flex-direction:column;align-items:center;gap:48px;text-align:center}
.story h2{margin:0;font-size:48px;line-height:1;color:var(--red)}
.story-img{width:350px;height:165px;object-fit:contain}
.story-copy{display:flex;flex-direction:column;gap:22.5px}  /* Figma's blank line between the paragraphs is one 15/1.5 line */
.story-copy p{margin:0;font-size:15px;line-height:1.5}
.story-copy strong{font-weight:700}

/* ---- image marquee (Figma 13937:8159) ----
   Full bleed out of the 500px content column; app.js clones the set and derives the
   duration from the measured track width, so the drift speed is the same whatever
   the viewport. */
/* Full bleed by cancelling .story's own 24px gutters — NOT 100vw. `vw` counts the
   scrollbar, so on desktop it overflows by ~15px, and html{overflow-x:clip} here
   means scrollWidth can never report it. The negative margin is exact at every
   width because the gutter is the same 24 on both breakpoints. */
.imgmq{position:relative;margin:156px -24px 0;overflow:hidden}
.imgmq-track{display:flex;width:max-content;will-change:transform;
  animation:imgScroll var(--mq-dur,60s) linear infinite}
.imgmq:hover .imgmq-track{animation-play-state:paused}
.imgmq-set{display:flex;gap:24px;padding-right:24px}
.imgmq-set img{height:300px;width:auto;max-width:none;border-radius:9.65px;object-fit:cover;flex:none}
@keyframes imgScroll{from{transform:translate3d(0,0,0)}to{transform:translate3d(-50%,0,0)}}
@media(prefers-reduced-motion:reduce){
  .imgmq-track{animation:none}
  .imgmq{overflow-x:auto}
}

/* ---------- first-paint entrance ----------
   Hero furniture eases up in sequence on load. The hidden start state is scoped to
   html.js-enter, which app.js adds at run time, so with JS off nothing is ever left
   invisible. --d carries each element's place in the stagger. */
.js-enter [data-enter]{opacity:0;transform:translateY(22px)}
.js-enter [data-enter].is-in{opacity:1;transform:none;
  transition:opacity .55s cubic-bezier(.65,.01,.05,.99) var(--d,0s),
             transform .55s cubic-bezier(.65,.01,.05,.99) var(--d,0s)}
@media(prefers-reduced-motion:reduce){
  .js-enter [data-enter],
  .js-enter [data-enter].is-in{opacity:1;transform:none;transition:none}
}

/* ---------- scroll reveal ---------- */
.js-reveal [data-reveal]{opacity:0;transform:translateY(28px)}
.js-reveal [data-reveal].is-in{opacity:1;transform:none;will-change:auto;
  transition:opacity .9s cubic-bezier(.65,.01,.05,.99) var(--d,0s),
             transform .9s cubic-bezier(.65,.01,.05,.99) var(--d,0s)}
@media(prefers-reduced-motion:reduce){
  .js-reveal [data-reveal],
  .js-reveal [data-reveal].is-in{opacity:1;transform:none;transition:none}
}

/* ---------- star-anise rail — the section divider ----------
   Lifted verbatim from the Christmas bundle. --anise-w is the ONE size knob; app.js
   reads it to set the pitch. --anise-show/--anise-h must stay at the 0.44524 crop
   ratio — that is the cut that lands just above the glyph's stray centre dot. */
.anise-rail{--anise-w:46.0px;--anise-h:44.23px;--anise-show:19.69px;
  position:absolute;left:0;right:0;bottom:0;height:var(--anise-show);z-index:5;pointer-events:none}
.anise-rail[hidden]{display:none}
.anise-rail.is-divider{overflow:hidden}
.anise-flake{position:absolute;bottom:calc(var(--anise-show) - var(--anise-h));
  width:var(--anise-w);height:var(--anise-h);will-change:translate,opacity}
.anise-glyph{display:block;width:100%;height:100%;background-repeat:no-repeat;background-size:100% 100%;
  will-change:translate,rotate}
.anise-flake.is-red .anise-glyph{background-image:url(assets/anise-red.svg)}
.anise-flake.is-green .anise-glyph{background-image:url(assets/anise-green.svg)}
.anise-rail.is-falling .anise-flake{animation:aniseDrop var(--dur,3s) linear var(--d,0s) both}
.anise-rail.is-falling .anise-glyph{
  animation:aniseSway var(--dur,3s) ease-in-out var(--d,0s) both,
            aniseSpin var(--dur,3s) linear var(--d,0s) both}
@keyframes aniseDrop{
  0%  {translate:0 calc(var(--fall,700px) * -1); opacity:0}
  5%  {opacity:1}
  100%{translate:0 0; opacity:1}
}
@keyframes aniseSway{
  0%  {translate:calc(var(--dx,26px) * -1) 0}
  26% {translate:var(--dx,26px) 0}
  52% {translate:calc(var(--dx,26px) * -.6) 0}
  78% {translate:calc(var(--dx,26px) * .3) 0}
  100%{translate:0 0}
}
@keyframes aniseSpin{from{rotate:var(--rot,180deg)}to{rotate:0deg}}
@media(prefers-reduced-motion:reduce){
  .anise-rail.is-falling .anise-flake,
  .anise-rail.is-falling .anise-glyph{animation:none}
}

/* ================= RESPONSIVE =================
   One breakpoint, as on the Christmas page: 1200. At and below it the hero is a
   382px column, too narrow for the 650px counter row, so the row stacks there too. */

/* the counter row stacks with the mobile layout */
@media(max-width:1200px){
  .counter{flex-direction:column;align-items:center;margin-top:48px}
  .goal{margin:0 0 49px}
  .dial{margin:0 0 25px}
  .counter-cap{width:182px;text-align:center}
}

@media(max-width:1200px){
  /* ---- nav: hamburger, Figma 13510:13703 ---- */
  .nav{min-height:0;align-items:stretch;display:block}
  .nav-in{display:grid;grid-template-columns:1fr auto;align-items:center;gap:0;
    max-width:none;padding:24px;column-gap:24px}
  .nav-logo{grid-column:1;grid-row:1;width:81.43px;height:18px}
  /* negative margins pull the 4px hit-area padding out of the layout, so the GLYPH
     lands 24px off the edge and the 18px logo sets the row height — a 66px bar. */
  .nav-burger{grid-column:2;grid-row:1;display:block;margin:-4px -4px -4px 0;line-height:0}
  .nav-burger img{width:18px;height:12px;display:block}
  .nav-links{grid-column:1/-1;grid-row:2;display:flex;flex-direction:column;gap:24px;
    width:100%;padding:0;opacity:0;max-height:0;overflow:hidden;
    transition:max-height .52s cubic-bezier(.16,1,.3,1),
               opacity .34s cubic-bezier(.16,1,.3,1),
               margin-top .52s cubic-bezier(.16,1,.3,1);
    margin-top:0;pointer-events:none}
  .nav-links.open{max-height:480px;opacity:1;margin-top:24px;pointer-events:auto}
  .nav-left,.nav-right{min-height:0;display:flex;flex-direction:column;
    align-items:flex-start;gap:24px;width:100%;margin:0}
  .nav-links a{font-family:'Funkis A',system-ui,sans-serif;font-weight:700;font-size:16px;line-height:23px;
    color:var(--plate);padding:0;width:auto;
    transform:translateY(-6px);transition:transform .52s cubic-bezier(.16,1,.3,1)}
  .nav-links.open a{transform:none}

  /* ---- hero, Figma 13934:2773 (430 frame) ----
     60px headline over two lines, the stack centred in a 382px column. */
  .hero{padding:clamp(24px,4svh,48px) 24px clamp(40px,6svh,124px);--nav-h:66px}
  .hero-in{max-width:382px}
  /* Figma 13934:2791 is a 300x90 box in a 430 frame — the headline is MEANT to
     break over two lines, so the cap is part of the design, not an accident. */
  .hero h1{font-size:60px;line-height:.75;max-width:300px}
  .lockup{margin-top:48px;gap:24px}
  .counter{margin-top:clamp(28px,4svh,48px)}
  .progress{margin-top:clamp(28px,4svh,50px)}

  /* ---- story, Figma 13934:2804 ---- */
  .story{padding:102px 24px 46px}
  .story-in{max-width:382px}
  .story h2{font-size:48px}
  .story-img{width:100%;max-width:350px;height:auto;aspect-ratio:350/165}
  .imgmq{margin-top:102px}
}

/* 430 and under: the headline has to hold two lines inside the gutters */
@media(max-width:430px){
  .hero h1{font-size:clamp(44px,14vw,60px)}
  .story h2{font-size:clamp(36px,11.2vw,48px)}
  .imgmq-set img{height:220px}
  .imgmq-set{gap:16px;padding-right:16px}
}

/* ---------- footer — mirrors grain.com.sg's grid + breakpoints ---------- */
.footer{background:var(--red-dark);color:var(--plate);position:relative;z-index:10}
.footer a{color:var(--plate);text-decoration:none}
.footer-main{width:100%;max-width:72rem;margin:0 auto;padding:3rem 1.5rem 0;display:flex;flex-wrap:wrap;align-items:flex-start}
.foot-left{box-sizing:border-box;width:16.66667%;padding:0 .5rem}
.foot-left img{width:36px}
.foot-right{box-sizing:border-box;min-width:0;width:83.33333%;padding:0 .5rem}
.foot-lists{display:flex;flex-wrap:wrap;width:100%}
.foot-list{box-sizing:border-box;width:20%;padding-right:1rem;margin-bottom:30px}
.foot-list.mailer{width:20%;padding-right:0}
.footer h4{margin:0 0 .25rem;color:#fff;font-weight:700;font-size:1rem;line-height:1.75rem;text-transform:uppercase}
.footer ul{margin:0 0 1.5rem;padding:0;list-style:none;line-height:1.75rem}
.footer li{padding-top:.5rem}
.footer a,.footer li{font-size:.875rem;line-height:1.25rem}
.grain-info{display:flex;align-items:flex-start;gap:1.5rem;margin-top:0;flex-wrap:wrap}
.grain-logos{display:flex;gap:1rem;align-items:center}
.grain-logos img{height:40px;width:auto}
.bottom-row{color:#fff;text-align:center;padding:1rem 0;min-height:80px}
.footer-divider{max-width:none;margin:1rem 0;height:1px;background-color:rgba(255,255,255,.25)}
.bottom-in{width:100%;max-width:72rem;margin:0 auto;padding:0 2rem;display:flex;align-items:center}
.foot-small{margin-left:16.66667%;line-height:1.85}
.foot-small a{font-size:.75rem;line-height:1rem;display:inline-block}
.foot-small a:not(:last-child){margin-right:1rem}
.foot-social{margin-left:1rem;line-height:1.85;display:flex;align-items:center}
.foot-social a{display:inline-flex;padding:0 .5rem;color:var(--plate)}

/* ---------- mobile footer — one centred column on the dark-red ground ---------- */
@media(max-width:1200px){
  .footer-main{padding:48px 24px 0;display:block;text-align:center}
  .foot-left{width:100%;padding:0 0 20px}
  .foot-left a{display:inline-block}
  .foot-left img{width:auto;height:50px}
  .foot-right{width:100%;padding:0}
  .foot-lists{display:block}
  .foot-list,.foot-list.mailer{width:100%;padding-right:0;margin:0 0 33px}
  .footer h4{font-size:14px;line-height:20px;margin:0}
  .footer ul{margin:0;line-height:normal}
  .footer li{padding-top:16px}
  .footer li a{font-size:14px;line-height:20px}
  .grain-info{flex-direction:column;align-items:center;text-align:center;gap:48px;margin-top:54px}
  .grain-logos{justify-content:center;gap:25px;align-items:center}
  .grain-logos img:first-child{height:40.3px}   /* halal 43.4 x 40.3 */
  .grain-logos img+img{height:38px}             /* iso   40.3 x 38   */
  .footer-divider{max-width:none;margin:0}
  .bottom-row{padding:0 0 48px;min-height:0}
  .bottom-in{flex-direction:column;gap:24px;padding:48px 24px 0}
  .foot-small{margin-left:0;line-height:normal;display:flex;flex-wrap:wrap;gap:12px;justify-content:center}
  .foot-small a{font-size:15px;line-height:normal;margin:0}
  .foot-social{float:none;margin:0;justify-content:center;width:100%;gap:13.5px}
  .foot-social a{padding:0}
  .foot-social svg{width:24px;height:24px}
}

/* ---------- mobile paper grain ----------
   The tile is 1520 real pixels. On a phone you only ever see ~430 of them, so the
   grain reads several times coarser than on desktop. Scaling the tile DOWN (never
   up) puts the density back; it is mirrored, so it still wraps seamlessly.
   Last in the file on purpose — media queries add no specificity, so this has to
   come after the base .paper rule. */
@media(max-width:1200px){
  .paper::after{background-size:460px 460px}
}
