/* ============================================================================
   CROMA MNL — public website
   Same warm Japandi palette as the POS and community app so all three read as one
   brand, retuned for a desktop-first marketing site.

   Design constraint worth knowing: there are no café photographs yet, so the
   visual interest has to come from type scale, colour, texture and spacing
   rather than imagery. Hence the layered gradients, the grain overlay and the
   oversized wordmark — they carry the page until real photos replace them.
   ========================================================================== */
:root{
  --bg:#e9e3d6; --surface:#fbf8f2; --surface2:#f1ebdd; --line:#ddd3c1;
  --text:#3a2b20; --muted:#8a7c66; --brand:#5b3f2c; --brand-2:#6f4e37;
  --accent:#b5895a; --accent-soft:#e8d9c3; --green:#6f8259; --red:#b0574f;
  --radius:18px; --radius-sm:12px;
  --shadow:0 4px 18px rgba(58,43,32,.07);
  --shadow-lg:0 18px 46px rgba(58,43,32,.14);
  --wrap:1140px;
  --ease:cubic-bezier(.22,.61,.36,1);
}
*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{
  background:var(--bg);color:var(--text);
  font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;
  -webkit-font-smoothing:antialiased;line-height:1.62;
}
img{max-width:100%;display:block}
a{color:var(--brand-2);text-decoration:none}
a:hover{text-decoration:underline}
.wrap{max-width:var(--wrap);margin:0 auto;padding:0 22px}
.muted{color:var(--muted)}
.small{font-size:13px}
.center{text-align:center}
h1,h2,h3,h4{line-height:1.15;letter-spacing:-.015em}

/* Fade-in-on-scroll. NOTE the `.js` guard: the hidden start state applies ONLY once the script
   has run and added that class. Without it the content is plain visible — a broken script or a
   browser without IntersectionObserver must never leave the page blank. Also disabled outright
   for anyone who asks for reduced motion. */
.js .rise{opacity:0;transform:translateY(18px);transition:opacity .7s var(--ease),transform .7s var(--ease)}
.js .rise.in{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .rise{opacity:1;transform:none;transition:none}
}

/* ---- header ---- */
.site-head{position:sticky;top:0;z-index:30;background:rgba(233,227,214,.86);
  backdrop-filter:saturate(150%) blur(12px);border-bottom:1px solid rgba(221,211,193,.8);
  transition:box-shadow .25s var(--ease)}
.site-head.stuck{box-shadow:0 4px 20px rgba(58,43,32,.09)}
.site-head .wrap{display:flex;align-items:center;gap:20px;height:80px}
/* The logo was previously 30px and vanished. Fluid, and much larger. */
.site-head picture{display:flex;align-items:center}
.site-head .logo{height:clamp(44px,4.6vw,56px);width:auto}
.site-nav{margin-left:auto;display:flex;gap:26px;align-items:center}
.site-nav a{color:var(--text);font-weight:600;font-size:15.5px;position:relative;padding:4px 0}
.site-nav a:not(.btn):hover{text-decoration:none}
.site-nav a:not(.btn)::after{content:'';position:absolute;left:0;right:100%;bottom:0;height:2px;
  background:var(--accent);transition:right .3s var(--ease)}
.site-nav a:not(.btn):hover::after,.site-nav a.active::after{right:0}
.navtoggle{display:none;margin-left:auto;background:var(--surface);border:1px solid var(--line);
  border-radius:12px;padding:9px 13px;font-size:18px;cursor:pointer;color:var(--text)}

/* ---- buttons ---- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;
  padding:14px 26px;border-radius:13px;font-weight:700;font-size:15.5px;
  border:1px solid var(--line);background:var(--surface);color:var(--text);
  cursor:pointer;transition:transform .18s var(--ease),box-shadow .18s var(--ease),background .18s}
.btn:hover{text-decoration:none;transform:translateY(-2px);box-shadow:var(--shadow-lg)}
.btn.primary{background:var(--brand);border-color:var(--brand);color:#faf6ee}
.btn.primary:hover{background:var(--brand-2)}
.btn.accent{background:var(--accent);border-color:var(--accent);color:#fff}
.btn.lg{padding:17px 34px;font-size:17px}

/* ---- hero ---- */
.hero{position:relative;overflow:hidden;padding:88px 0 78px;text-align:center;
  background:
    radial-gradient(900px 420px at 50% -12%,rgba(181,137,90,.28),transparent 70%),
    radial-gradient(600px 340px at 12% 108%,rgba(111,78,55,.14),transparent 70%),
    linear-gradient(180deg,#efe9dd,#e9e3d6);
}
/* Faint grain so the large flat areas don't read as an unfinished template. */
.hero::after{content:'';position:absolute;inset:0;pointer-events:none;opacity:.5;
  background-image:radial-gradient(rgba(91,63,44,.12) 1px,transparent 1px);
  background-size:26px 26px}
.hero .inner{position:relative;z-index:1}
.hero picture{display:block;width:-moz-fit-content;width:fit-content;margin:0 auto}
.hero .mark{height:clamp(96px,12vw,150px);width:auto;margin:0 0 28px;
  filter:drop-shadow(0 6px 18px rgba(58,43,32,.14))}
.hero h1{font-size:clamp(31px,5.4vw,54px);color:var(--brand);max-width:16ch;margin:0 auto}
.hero p.lead{font-size:clamp(16.5px,1.9vw,20px);color:#6b5b47;max-width:60ch;margin:22px auto 0}
.hero .cta{display:flex;gap:13px;justify-content:center;flex-wrap:wrap;margin-top:34px}
.hero .quickfacts{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin-top:34px}
.pill{background:rgba(251,248,242,.86);border:1px solid var(--line);border-radius:999px;
  padding:9px 17px;font-size:14px;font-weight:600;display:inline-flex;align-items:center;gap:8px}

/* ---- sections ---- */
section{padding:76px 0}
.alt{background:var(--surface2)}
.dark{background:linear-gradient(160deg,#5b3f2c,#4a3223);color:#f0e6d8}
.sec-head{text-align:center;max-width:66ch;margin:0 auto 44px}
.sec-head .eyebrow{font-size:12.5px;font-weight:800;letter-spacing:.16em;text-transform:uppercase;
  color:var(--accent);margin-bottom:12px}
.sec-head h1,.sec-head h2{font-size:clamp(25px,3.6vw,38px);color:var(--brand)}
.sec-head p{color:var(--muted);margin-top:12px;font-size:17px}

/* ---- cards ---- */
.grid{display:grid;gap:20px}
.g2{grid-template-columns:repeat(auto-fit,minmax(330px,1fr))}
.g3{grid-template-columns:repeat(auto-fit,minmax(285px,1fr))}
.g4{grid-template-columns:repeat(auto-fit,minmax(210px,1fr))}
.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);
  padding:26px;box-shadow:var(--shadow);transition:transform .22s var(--ease),box-shadow .22s var(--ease)}
.card.hoverable:hover{transform:translateY(-4px);box-shadow:var(--shadow-lg)}
.card .ic{font-size:30px;line-height:1;margin-bottom:14px}
.card h3{font-size:19.5px;color:var(--brand);margin-bottom:9px}
.card p{color:var(--muted);font-size:15px}
.dark .card{background:rgba(255,255,255,.07);border-color:rgba(255,255,255,.14);box-shadow:none}
.dark .card p{color:#cdbca6}

/* ---- dark-panel overrides ----------------------------------------------------------------
   These MUST come after .sec-head h2 / .card h3 / .card p and carry higher specificity, or those
   later brand-brown rules win at equal specificity and render brown text on a brown panel. */
.dark .sec-head h1,.dark .sec-head h2,.dark .card h3,.dark h1,.dark h2,.dark h3{color:#fff}
.dark .sec-head p,.dark .card p,.dark .muted{color:#d8c9b3}
.dark .sec-head .eyebrow{color:var(--accent-soft)}

/* amenity tiles — the work-friendly pitch */
.amen{display:flex;gap:14px;align-items:flex-start;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--radius);padding:20px}
.amen .ic{font-size:25px;flex:none;line-height:1.2}
.amen strong{display:block;font-size:16px;color:var(--brand)}
.amen span{font-size:14px;color:var(--muted)}

/* numbered steps */
.step{position:relative;padding-left:56px}
.step .n{position:absolute;left:0;top:-2px;width:40px;height:40px;border-radius:50%;
  background:var(--accent-soft);color:var(--brand);font-weight:800;font-size:17px;
  display:flex;align-items:center;justify-content:center}

/* ---- menu ---- */
.menu-cat{margin-top:44px}
.menu-cat h3{font-size:24px;color:var(--brand);padding-bottom:12px;
  border-bottom:2px solid var(--accent-soft);display:flex;align-items:baseline;gap:12px}
.menu-cat h3 .count{font-size:13px;font-weight:600;color:var(--muted);letter-spacing:.04em}
.menu-items{display:grid;gap:14px;margin-top:20px;grid-template-columns:repeat(auto-fill,minmax(230px,1fr))}
.mi{background:var(--surface);border:1px solid var(--line);border-radius:14px;overflow:hidden;
  transition:transform .2s var(--ease),box-shadow .2s var(--ease)}
.mi:hover{transform:translateY(-3px);box-shadow:var(--shadow-lg)}
/* Item photos appear automatically as soon as they're uploaded in the POS; until then the card
   is text-only and the grid still looks intentional rather than broken. */
.mi .ph{aspect-ratio:4/3;background:var(--surface2) center/cover no-repeat;
  border-bottom:1px solid var(--line)}
.mi .tx{padding:14px 16px}
/* The favourite mark, bottom-right of the card, on its own row with the Hot or
   iced tag. IN FLOW, not absolutely positioned — pinned to the corner it would
   sooner or later land on the last line of a long description, and only on the
   products whose text happens to run that far. margin-left:auto is what holds
   it right whether or not the tag is there to sit beside it.
   No halo down here: it is on the cream card, not on a photograph, where the
   wordmark brown measures 11.7:1 and needs no help. */
.mi .foot{display:flex;align-items:center;gap:8px;margin-top:8px}
.mi .foot .tag{margin-top:0}
.fav-foot{margin-left:auto;margin-right:0;flex:none}
.mi .n{font-weight:700;font-size:15.5px}
.mi .d{font-size:13px;color:var(--muted);margin-top:3px}
.mi .tag{display:inline-block;margin-top:8px;font-size:11.5px;font-weight:700;letter-spacing:.05em;
  text-transform:uppercase;color:var(--accent);background:var(--accent-soft);
  border-radius:6px;padding:3px 8px}
.menu-note{margin-top:38px;padding:22px;background:var(--surface);border:1px dashed var(--line);
  border-radius:var(--radius);text-align:center;color:var(--muted);font-size:15px}

/* ---- events ---- */
.ev{display:flex;gap:18px;align-items:flex-start;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--radius);padding:22px;box-shadow:var(--shadow);
  transition:transform .22s var(--ease),box-shadow .22s var(--ease)}
.ev:hover{transform:translateY(-3px);box-shadow:var(--shadow-lg)}
.ev .when{flex:none;width:76px;text-align:center;background:var(--accent-soft);
  border-radius:14px;padding:12px 6px}
.ev .when .d{font-size:26px;font-weight:800;color:var(--brand);line-height:1}
.ev .when .m{font-size:11.5px;font-weight:800;color:var(--brand-2);text-transform:uppercase;letter-spacing:.08em}
.ev h3{font-size:18.5px;color:var(--brand)}
.ev .meta{font-size:13px;color:var(--muted);margin-top:5px}
.ev p{font-size:14.5px;margin-top:10px;color:#5f5140}

/* ---- forms ---- */
label{display:block;font-size:13px;font-weight:700;color:var(--muted);margin:16px 0 7px}
input,textarea,select{width:100%;padding:13px 15px;border:1px solid var(--line);
  border-radius:12px;background:var(--surface);color:var(--text);font:inherit;font-size:15.5px}
input:focus,textarea:focus{outline:2px solid var(--accent);outline-offset:1px;border-color:var(--accent)}
textarea{min-height:130px;resize:vertical}
.formmsg{min-height:22px;font-size:14px;margin-top:11px}

/* ---- footer ---- */
.site-foot{background:linear-gradient(160deg,#4a3223,#3a2618);color:#e0d3c0;padding:58px 0 28px;margin-top:0}
.site-foot a{color:#f2e9dc}
.site-foot .cols{display:grid;gap:32px;grid-template-columns:1.4fr 1fr 1fr}
.site-foot h4{font-size:12.5px;text-transform:uppercase;letter-spacing:.14em;margin-bottom:14px;color:#fff}
.site-foot picture{display:block}
.site-foot .flogo{height:44px;width:auto;margin-bottom:14px;
  filter:brightness(0) invert(1) opacity(.92)}
.site-foot p,.site-foot li{font-size:14.5px;color:#cdbca6}
.site-foot .socials{display:flex;gap:11px;margin-top:16px}
.site-foot .socials a{width:40px;height:40px;border-radius:50%;background:rgba(255,255,255,.11);
  display:flex;align-items:center;justify-content:center;font-size:16px;font-weight:700;
  transition:background .2s}
.site-foot .socials a:hover{background:rgba(255,255,255,.22);text-decoration:none}
/* The marks are inline SVG using currentColor, so they inherit the footer's
   text colour and need no second palette. line-height:0 stops the anchor
   reserving a text line under a graphic that has no baseline. */
.site-foot .socials a{line-height:0;color:inherit}
.site-foot .socials svg{display:block}
.site-foot .legal{border-top:1px solid rgba(255,255,255,.15);margin-top:36px;padding-top:20px;
  font-size:13px;display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap;color:#b3a28c}
/* BIR Registration Seal Badge (owner, 7 Nov 2026). The BIR asks a business online to show it where it is easily seen
   and can be scanned; it is page 3 of the COR. On white, as printed, so its QR keeps full contrast on the dark footer. */
.site-foot .bir-seal{display:flex;gap:14px 18px;align-items:center;flex-wrap:wrap;margin-top:34px}
.site-foot .bir-seal a{display:block;line-height:0;background:#fff;border-radius:12px;padding:8px 10px;max-width:100%}
.site-foot .bir-seal img{display:block;width:440px;max-width:100%;height:auto}
.site-foot .bir-seal p{font-size:13px;margin:0;max-width:320px;color:#cdbca6}

/* loading placeholder for the live-fetched sections */
.skel{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);
  height:104px;animation:pulse 1.5s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:.5}50%{opacity:.85}}

@media(max-width:820px){
  .site-foot .cols{grid-template-columns:1fr}
}
@media(max-width:760px){
  .site-head .wrap{height:70px}
  .site-nav{display:none;position:absolute;top:70px;left:0;right:0;background:var(--surface);
    flex-direction:column;gap:0;border-bottom:1px solid var(--line);box-shadow:var(--shadow-lg);padding:6px 0}
  .site-nav.open{display:flex}
  .site-nav a{padding:15px 22px;width:100%}
  .site-nav a:not(.btn)::after{display:none}
  .site-nav .btn{margin:10px 22px}
  .navtoggle{display:block}
  .ev{flex-direction:column;gap:14px}
  .ev .when{width:auto;display:inline-flex;gap:9px;align-items:baseline;padding:8px 14px}
  section{padding:52px 0}
  .hero{padding:56px 0 54px}
}

/* ---- photo gallery -------------------------------------------------------------------------
   Rendered by site.js only for files that actually load, so dropping photo-1.jpg … photo-6.jpg
   into assets/ makes them appear with no markup change, and missing ones never leave a gap. */
#galleryWrap{display:none}
#galleryWrap.has{display:block}
.gal{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}
.gal figure{margin:0;border-radius:var(--radius);overflow:hidden;border:1px solid var(--line);
  box-shadow:var(--shadow);background:var(--surface2)}
.gal img{width:100%;height:100%;aspect-ratio:4/3;object-fit:cover;
  transition:transform .5s var(--ease)}
.gal figure:hover img{transform:scale(1.04)}
.gal figure.wide{grid-column:span 2}
@media(max-width:700px){ .gal figure.wide{grid-column:auto} }

/* Hero photo, when assets/hero.jpg exists. The overlay keeps the headline readable over any
   image — without it, a bright photo would wash the type out completely. */
/* The photo sits on its own layer, .hero-bg, rather than on the section, so js/motion.js can
   drift it at a different speed from the copy. It is taller than the hero (inset -15% top and
   bottom) so the drift never shows an edge. The section keeps a dark fill underneath, so the
   white type is legible in the moment before the photo paints. */
.hero.has-photo{background:#3a2b20}
.hero-bg{display:none;position:absolute;inset:-15% 0;z-index:-2;pointer-events:none;
  background:linear-gradient(rgba(58,43,32,.62),rgba(58,43,32,.72)),var(--heroimg) center/cover no-repeat;
  will-change:transform}
.hero.has-photo .hero-bg{display:block}
.hero.has-photo::after{display:none}
.hero.has-photo h1{color:#fff;text-shadow:0 2px 18px rgba(0,0,0,.35)}
.hero.has-photo p.lead{color:#f0e6d8}
.hero.has-photo .mark{filter:brightness(0) invert(1) drop-shadow(0 4px 16px rgba(0,0,0,.4))}
.hero.has-photo .pill{background:rgba(251,248,242,.92)}
/* ---- seasonal themes ----------------------------------------------------------------------
   js/seasons.js sets the colour tokens and <html data-season>; these are the few things a token
   cannot do. Spec: docs/superpowers/specs/2026-11-01-seasonal-themes-design.md */
html[data-season] .hero{background:
    radial-gradient(900px 420px at 88% -12%,var(--season-glow1),transparent 70%),
    radial-gradient(640px 360px at 8% 108%,var(--season-glow2),transparent 70%),
    var(--bg)}
html[data-season] .hero.has-photo{background:#3a2b20}
html[data-season] .hero-beans{display:none}
html[data-season] .hero:not(.has-photo) p.lead{color:var(--muted)}
.season-art{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none;z-index:0}
/* On the hero photo the drawing stays, in light lines: --brand is the deep colour the
   drawings stroke with, so it is turned cream here; the accent (gold, rose, mango...) stays. */
.hero.has-photo .season-art{--brand:#fbf6ee}
.hero.has-photo .season-paper{display:none}
.season-greet{display:flex;align-items:center;justify-content:center;gap:12px;margin:0 auto 16px;
  font-family:'Fraunces',Georgia,serif;font-style:italic;font-size:clamp(15px,1.7vw,18px);line-height:1.3;color:var(--brand)}
.season-greet::before,.season-greet::after{content:'';flex:none;width:34px;height:1px;background:var(--accent)}
.season-greet[hidden]{display:none}
.hero.has-photo .season-greet{color:#fff;text-shadow:0 2px 14px rgba(0,0,0,.35)}
/* A season's accent is a light gold, mango or yellow: white text on it reads at about 2:1, so while a
   season is on, the components that put text on the accent use the season's deep colour instead.
   The same goes for small labels set in the accent. (The eyebrow on the dark band is cream: left alone.) */
html[data-season] .btn.accent{background:var(--brand);border-color:var(--brand)}
html[data-season] .dark .btn.accent{border-color:var(--accent)}  /* a ring, so the deep button does not sink into the brown band */
html[data-season] section:not(.dark) .sec-head .eyebrow,
html[data-season] .mi .tag,
html[data-season] .tour-n{color:var(--brand)}

/* ==========================================================================
   DESIGN REFRESH
   ==========================================================================
   The palette was already right — warm, Japandi, unmistakably a café. What made
   the page read as unfinished was that every word on it was set in the system UI
   font: the same face as a settings dialog. A brand cannot sound like anything
   in Segoe UI.

   So: one display face, self-hosted, headings only.

   Self-hosted because the CSP is font-src 'self' data: — Google's CDN is not on
   that list and a <link> to it would be blocked outright, silently falling back
   to the system font on the live site while looking perfect locally. Serving it
   ourselves is also faster and leaves no third party in the page.

   Headings only because body text in a good system stack is genuinely fine, and
   one 67 kB file is a fair price for the whole brand voice. Two would not be.

   AMENDED: there are now two, and the paragraph above is the argument against
   it, so it is worth saying what changed. The hero headline is set in Poppins
   at the owner's request — a geometric sans against the Fraunces serif used by
   every other heading. That is a deliberate pairing, not a drift: one face for
   the one line that has to land, the other for the rest of the page.

   The cost is kept honest. Poppins ships as ONE weight and ONE subset — 500,
   latin — because exactly one element uses it, so it is 7.6 kB rather than the
   ~170 kB the full family would be. Self-hosted for the same reason Fraunces
   is: font-src is 'self' data:, so a <link> to Google would be blocked on the
   live site and fall back silently while looking perfect locally.
   ========================================================================== */

@font-face{
  font-family:'Fraunces';
  src:url('../assets/fonts/fraunces-latin.woff2') format('woff2-variations');
  font-weight:400 700;                 /* variable: one file, every weight */
  font-display:swap;                   /* text is readable before the font lands */
  font-style:normal;
}

/* Poppins is NOT a variable font on Google Fonts — it is 18 separate static
   files — so this is the single weight the headline actually uses. Asking for
   any other weight would synthesise it (a smeared faux-bold), which is why the
   rule below sets font-weight:500 explicitly rather than inheriting.

   500 rather than 600, which is what the Fraunces headline used. At 54px the
   two were set side by side and 600 reads blunt — evenly heavy strokes with the
   counters closing up — while 500 keeps the geometry open. Against a delicate
   serif everywhere else and a warm, quiet palette, the lighter cut is the one
   that belongs. The hero photo sits under a dim overlay and the headline
   already carries a text-shadow, so nothing is lost in legibility. */
@font-face{
  font-family:'Poppins';
  src:url('../assets/fonts/poppins-500-latin.woff2') format('woff2');
  font-weight:500;
  font-display:swap;
  font-style:normal;
  /* Matches the subset that was downloaded. Without it the browser would load
     this file to render a character it does not contain and show a blank. */
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

:root{
  --display:'Fraunces', Georgia, 'Times New Roman', serif;
  /* A fluid scale, so the page is composed at every width instead of at two
     breakpoints with a jump between them. */
  /* CAPPED WELL BELOW THE OLD 4.6rem. That ceiling was set for Fraunces, a
     serif with fine strokes and a small x-height that can carry 74px without
     dominating. Poppins is a geometric sans at 600: wider, evenly heavy, and
     with a tall x-height, so the same number reads roughly a size larger and
     started competing with the wordmark instead of sitting under it.
     54px is where the headline supports the mark rather than fighting it — and
     is the ceiling this hero had before the refresh, arrived at independently.
     The floor is unchanged, so phones look exactly as they did. */
  --step-hero:clamp(2.4rem, 1.5rem + 2.4vw, 3.375rem);
  --step-h2:clamp(1.75rem, 1.1rem + 2.2vw, 2.9rem);
  --step-h3:clamp(1.05rem, .95rem + .45vw, 1.28rem);
  --step-lead:clamp(1.02rem, .95rem + .5vw, 1.28rem);
}

h1,h2,h3,.hero h1,.sec-head h1,.sec-head h2{
  font-family:var(--display);
  font-weight:600;
  letter-spacing:-.015em;              /* a serif at display size needs tightening */
  line-height:1.12;
  text-wrap:balance;                   /* no orphaned last word in a headline */
}
h3{line-height:1.25}

/* THE ONE LINE IN POPPINS. Set here rather than on --display, so it applies to
   this headline and nothing else on the site. */
.hero h1{
  font-family:'Poppins', var(--display);
  font-size:var(--step-hero);
  font-weight:500;
  /* 'opsz' is a FRAUNCES axis and there is no such axis in Poppins — it is a
     static face, so the declaration would sit here doing nothing and reading as
     if it did something. Dropped with the serif it belonged to. */
  /* -.022em was tightening for a serif at display size. Poppins is a geometric
     sans whose round counters close up fast when tracked in; at 4.6rem the same
     value pulls the o/e/c together. Eased, not removed — a display line still
     wants slightly less than default. */
  letter-spacing:-.012em;
  /* 1.12 comes from the shared heading rule and is a SERIF's leading. Poppins
     has a tall x-height, so its lines sit visually closer than the number says
     and a three-line stack packs into a slab. A little more air separates the
     lines without opening a gap. */
  line-height:1.2;
  /* 16ch was measured in Fraunces. The same count in Poppins is a wider box,
     and at the smaller size above it is what decides whether this breaks into
     three lines or a ragged four. */
  max-width:17ch;
}
.sec-head h1,.sec-head h2{font-size:var(--step-h2)}
.card h3,.amen strong{font-size:var(--step-h3)}
.hero p.lead{font-size:var(--step-lead);line-height:1.62;max-width:44ch}
.sec-head p{max-width:62ch;margin-left:auto;margin-right:auto}

/* The eyebrow was doing nothing. Small caps and letterspacing make it read as a
   label rather than as a stray line of text above the headline. */
.eyebrow{
  font-family:var(--display);
  font-weight:600;font-size:12px;letter-spacing:.16em;text-transform:uppercase;
  opacity:.9;
}

/* --- HEADER: floats over the hero, frosts once you scroll past it ---------- */
.site-head{
  backdrop-filter:saturate(1.4) blur(10px);
  -webkit-backdrop-filter:saturate(1.4) blur(10px);
  transition:background-color .28s ease, box-shadow .28s ease, padding .28s ease;
}
.site-head.stuck{box-shadow:0 1px 0 rgba(58,43,32,.09), 0 10px 30px -18px rgba(58,43,32,.45)}
.site-nav a:not(.btn){font-weight:600}

/* --- BUTTONS: the primary action should look pressable ------------------- */
.btn{
  transition:transform .16s cubic-bezier(.2,.8,.3,1), box-shadow .22s ease,
             background-color .22s ease, color .22s ease, border-color .22s ease;
  will-change:transform;
}
.btn:hover{transform:translateY(-1.5px)}
.btn:active{transform:translateY(0)}
.btn.primary{box-shadow:0 10px 24px -12px rgba(91,63,44,.75)}
.btn.primary:hover{box-shadow:0 16px 30px -14px rgba(91,63,44,.8)}
.btn.lg{border-radius:999px}          /* pill CTAs read as friendlier than boxes */
.btn:focus-visible{outline:2.5px solid var(--accent);outline-offset:3px}

/* --- HERO ---------------------------------------------------------------- */
.hero{position:relative;isolation:isolate}
/* A soft vignette so the headline sits on the darkest part of the photograph
   rather than fighting whatever the image happens to be doing behind it. */
.hero::before{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:radial-gradient(115% 85% at 50% 0%, rgba(0,0,0,0) 42%, rgba(35,25,18,.42) 100%);
}
.hero .mark{filter:brightness(0) invert(1);opacity:1;
  image-rendering:auto;}   /* full opacity: .94 only made the edges muddier */
.hero .quickfacts{gap:9px}

/* --- PILLS & CARDS ------------------------------------------------------- */
.pill{
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
  border-radius:999px;font-weight:600;letter-spacing:.005em;
}
.card{
  border-radius:var(--radius);
  transition:transform .22s cubic-bezier(.2,.8,.3,1), box-shadow .28s ease;
}
.card.hoverable:hover{transform:translateY(-4px);box-shadow:0 22px 44px -26px rgba(58,43,32,.6)}
.amen{transition:transform .2s ease}
.amen:hover{transform:translateX(2px)}

/* --- RHYTHM: give the page room to breathe ------------------------------- */
section{padding-block:clamp(58px, 4vw + 42px, 104px)}
.sec-head{margin-bottom:clamp(28px, 2vw + 18px, 46px)}

/* --- OPEN NOW: a live, factual signal, filled in by site.js -------------- */
/* THE OPENING BAR — the one thing on the site that is allowed to shout.
 *
 * The first version was a brown gradient, which on a brown-and-cream site is
 * camouflage: it read as part of the furniture and nobody's eye stopped on it.
 * A launch announcement has one job, so this one carries the café's own photo
 * under an espresso scrim, a warm glow that drifts, and a shine that sweeps
 * across every few seconds. The gold is deliberately NOT in the everyday
 * palette — that is what makes it read as an event rather than as chrome.
 *
 * The scrim is part of the background shorthand rather than a moving layer, so
 * the photo can never show through underneath the text while the glow drifts.
 * Contrast is carried by the scrim, not by the animation: with motion off, or
 * before the photo loads, the bar is still cream-on-espresso and perfectly
 * legible. Everything moving is decoration on top of something that already
 * works. */
.openbar{
  position:relative;overflow:hidden;isolation:isolate;
  display:flex;align-items:center;justify-content:center;gap:12px;
  padding:13px 18px;min-height:54px;z-index:30;
  color:#fbf8f2;text-align:center;font-size:14.5px;font-weight:600;letter-spacing:.2px;line-height:1.35;
  background:
    linear-gradient(90deg,rgba(24,16,11,.94),rgba(24,16,11,.68) 50%,rgba(24,16,11,.94)),
    url('../assets/hero.jpg') center 62% / cover no-repeat,
    #24180f;
  box-shadow:inset 0 -1px 0 rgba(251,248,242,.14);
}
/* The drifting glow. Two soft pools that slide past each other, so the light
   never sits still long enough to become a texture. */
.openbar::before{
  content:'';position:absolute;inset:-60% -20%;z-index:-1;pointer-events:none;
  background:
    radial-gradient(38% 120% at 28% 50%,rgba(245,198,116,.50),transparent 62%),
    radial-gradient(34% 120% at 72% 50%,rgba(181,137,90,.45),transparent 62%);
  animation:openGlow 11s ease-in-out infinite alternate;
}
/* The shine. A single pass, then a long pause — a sweep every two seconds is
   a nightclub, one every seven reads as a sheen. */
.openbar::after{
  content:'';position:absolute;top:-60%;bottom:-60%;width:22%;z-index:-1;pointer-events:none;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.30),transparent);
  animation:openShine 7s cubic-bezier(.5,0,.5,1) infinite;
}
@keyframes openGlow{ from{transform:translateX(-9%)} to{transform:translateX(9%)} }
@keyframes openShine{ 0%{transform:translateX(-160%) skewX(-16deg)} 55%,100%{transform:translateX(560%) skewX(-16deg)} }

.openbar .ob-dot{
  width:9px;height:9px;border-radius:50%;flex:none;background:#f5c674;
  box-shadow:0 0 0 4px rgba(245,198,116,.22),0 0 12px rgba(245,198,116,.9);
  animation:openPulse 2.2s ease-in-out infinite;
}
.openbar.open .ob-dot{background:#8ed08a;box-shadow:0 0 0 4px rgba(142,208,138,.22),0 0 12px rgba(142,208,138,.9)}
@keyframes openPulse{ 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:.55;transform:scale(.82)} }

/* The date itself, which is the whole message. */
.openbar b{
  font-weight:800;letter-spacing:.3px;
  background:linear-gradient(90deg,#ffd489,#f5c674 45%,#ffe3b0);
  -webkit-background-clip:text;background-clip:text;color:transparent;
  /* A gradient-clipped text has no colour of its own if the clip is not
     supported, so a fallback colour is painted first by the rule above it. */
}
.openbar.open b{background:linear-gradient(90deg,#b7e8b0,#8ed08a 45%,#d6f5cf);-webkit-background-clip:text;background-clip:text}
@supports not ((-webkit-background-clip:text) or (background-clip:text)){
  .openbar b{color:#ffd489;background:none}
  .openbar.open b{color:#b7e8b0;background:none}
}

@media (max-width:560px){
  .openbar{font-size:13px;padding:11px 14px;min-height:48px;gap:9px}
  .openbar::after{width:34%}
}
@media (prefers-reduced-motion:reduce){
  .openbar::before,.openbar::after,.openbar .ob-dot{animation:none}
  .openbar::after{opacity:0}
}

.openbadge{
  display:none;align-items:center;gap:8px;margin:20px auto 0;
  padding:8px 16px;border-radius:999px;font-size:13.5px;font-weight:600;
  background:rgba(251,248,242,.14);border:1px solid rgba(251,248,242,.3);
  color:#fbf8f2;backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
  width:max-content;
}
.openbadge.show{display:inline-flex}
.openbadge .dot{width:8px;height:8px;border-radius:50%;background:#8ec07c;
  box-shadow:0 0 0 4px rgba(142,192,124,.22)}
.openbadge.shut .dot{background:#d98b7f;box-shadow:0 0 0 4px rgba(217,139,127,.2)}

@media (prefers-reduced-motion: reduce){
  .btn:hover,.card.hoverable:hover,.amen:hover{transform:none}
  .btn,.card,.amen{transition:none}
}

/* --- SCAN TO JOIN -------------------------------------------------------- */
.joinrow{display:flex;gap:30px;align-items:center;justify-content:center;
  flex-wrap:wrap;margin-top:40px;text-align:left}
.qrtile{margin:0;padding:14px;border-radius:20px;background:var(--surface);
  line-height:0;box-shadow:0 18px 40px -22px rgba(0,0,0,.55);flex:none}
.qrtile img{display:block;width:180px;height:180px}
.joincopy{max-width:34ch}
.joincopy h3{margin:0 0 8px;font-size:var(--step-h3)}
.joincopy p{margin:0 0 18px;opacity:.85}
.joincopy .btn{margin:0}
/* A code on the screen you are holding is not useful — on a phone the button is
   the whole answer, so the tile goes and the copy re-centres. */
@media(max-width:640px){
  .qrtile{display:none}
  .joinrow{text-align:center}
  .joincopy{max-width:none}
}

/* The Croma Favorite mark. Sits inline beside a product name on every surface
   — till, community app, website, admin list — so it reads the same way
   wherever a customer meets it. Rounded to match the source SVG's own corner
   radius, and nudged up so it sits on the text baseline rather than under it.
   Sits BEFORE the name, so the gap belongs on its right. */
.fav-mark{display:inline-block;border-radius:4px;vertical-align:-2px;
  margin-right:7px;flex:none}

/* ==========================================================================
   SCROLL MOTION — driven by js/motion.js
   ==========================================================================
   Everything below works without the script. The room tour is the whole room
   with its stops listed underneath until motion.js adds .live, and the step
   numbers only gain a glow. Reduced motion never gets .live at all, and the
   beans' idle bob stops for it too. */

/* --- THE ROOM TOUR ------------------------------------------------------- */
.tour{max-width:1040px;margin:0 auto clamp(34px,3vw + 18px,56px)}
.tour-stage{position:relative;overflow:hidden;isolation:isolate;
  border-radius:calc(var(--radius) + 4px);aspect-ratio:1916/1281;background:#2b2018;
  box-shadow:0 30px 60px -34px rgba(58,43,32,.6)}
/* A soft inner shade, so the edges of a zoomed-in crop fall away like a lens
   rather than stopping at a hard line. */
.tour-stage::after{content:'';position:absolute;inset:0;z-index:1;pointer-events:none;
  border-radius:inherit;box-shadow:inset 0 0 90px rgba(24,16,11,.34)}
/* The image is a flex item sized by the stage's HEIGHT and centred, so when a
   phone makes the stage taller than the picture's own shape, it overflows
   evenly left and right instead of squashing. motion.js reads both boxes to
   frame each stop, whatever shape the stage is. */
.tour-stage{display:flex;justify-content:center;align-items:center}
.tour-stage picture{display:contents}
.tour-img{flex:none;height:100%;width:auto;max-width:none;transform-origin:50% 50%;will-change:transform}
/* Says what the picture is. It is a render of the room, not a photograph of
   it, and the page should not let anyone think otherwise. */
.tour-tag{position:absolute;top:14px;right:14px;z-index:2;font-size:12px;font-weight:700;
  letter-spacing:.03em;color:#fbf8f2;background:rgba(24,16,11,.55);border-radius:999px;padding:6px 12px;
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}

.tour-stops{list-style:none;display:grid;gap:14px;margin-top:18px;
  grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}
.tour-stops li{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-sm);
  padding:16px 18px}
.tour-n{display:block;font-size:11.5px;font-weight:800;letter-spacing:.12em;text-transform:uppercase;
  color:var(--accent);margin-bottom:6px}
.tour-stops strong{display:block;font-family:var(--display);font-weight:600;font-size:var(--step-h3);
  color:var(--brand);line-height:1.25}
.tour-stops li>span:last-child{display:block;font-size:14.5px;color:var(--muted);margin-top:6px}

/* LIVE: pinned, one stop at a time. The stops share one grid cell, so the box
   is as tall as the longest and nothing below it jumps as they swap. The
   width is capped by the viewport HEIGHT too, so the pinned picture always
   fits under the header on a short laptop screen. */
.tour.live{max-width:min(1040px,calc((100svh - 160px) * 1.49))}
.tour.live .tour-stops{grid-template-columns:1fr;gap:0;margin-top:14px}
.tour.live .tour-stops li{grid-area:1/1}
/* A phone is taller than it is wide, so a 3:2 picture pinned in the middle of
   it is a letterbox with empty screen above and below. The stage takes half
   the screen instead; motion.js opens on the whole room fitted inside it and
   zooms in to fill. */
@media (max-width:760px){
  .tour.live .tour-stage{aspect-ratio:auto;height:min(50svh,440px)}
  /* The bands above and below the whole-room view, filled with the same
     picture blurred and dimmed rather than left as flat dark. Same file as
     the <img>, so it costs no second download. Phones only, so a laptop never
     fetches it for a band it does not have. */
  .tour.live .tour-stage::before{content:'';position:absolute;inset:-30px;z-index:-1;
    background:url('../assets/room-3d.jpg') center/cover no-repeat;
    background-image:image-set(url('../assets/room-3d.webp') type('image/webp'),url('../assets/room-3d.jpg') type('image/jpeg'));
    filter:blur(22px) brightness(.55) saturate(1.1)}
}
@media (min-width:761px){
  .tour.live{position:relative}
  .tour.live .tour-stops{position:absolute;z-index:2;margin:0;
    left:clamp(14px,2.2vw,26px);bottom:clamp(14px,2.2vw,26px);width:min(400px,calc(100% - 28px))}
  .tour.live .tour-stops li{background:rgba(251,248,242,.92);border-color:rgba(255,255,255,.6);
    box-shadow:0 18px 40px -20px rgba(0,0,0,.55);
    backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}
}
/* Where you are in the tour. Built by motion.js, so it only exists when live. */
.tour-dots{position:absolute;left:14px;top:14px;z-index:2;display:flex;gap:7px;padding:8px 10px;
  border-radius:999px;background:rgba(24,16,11,.5);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}
.tour-dots i{width:7px;height:7px;border-radius:50%;background:rgba(251,248,242,.4);
  transition:background .3s,transform .3s}
.tour-dots i.on{background:#fbf8f2;transform:scale(1.3)}

/* --- HERO BEANS: rendered in 3D, floating at their own depths ------------- */
.hero-beans{position:absolute;inset:0;z-index:0;pointer-events:none}
/* Two elements per bean on purpose: motion.js moves the span on scroll, CSS
   bobs the picture inside it, so one element never has two owners for its
   transform. */
.bean{position:absolute;left:var(--x);top:var(--y);width:var(--s);will-change:transform}
.bean picture{display:block;transform:rotate(var(--r));animation:beanBob 7s ease-in-out infinite alternate}
.bean img{display:block;width:100%;height:auto;filter:drop-shadow(0 8px 12px rgba(0,0,0,.35))}
.bean:nth-child(2n) picture{animation-duration:9s;animation-delay:-3s}
.bean:nth-child(3n) picture{animation-duration:11s;animation-delay:-5s}
/* Depth of field: far beans are small, soft and faint; the nearest is soft
   because it is too close to be in focus. */
.bean.far img{opacity:.75;filter:blur(1.2px) drop-shadow(0 6px 10px rgba(0,0,0,.3))}
.bean.near img{filter:blur(1.6px) drop-shadow(0 12px 16px rgba(0,0,0,.35))}
@keyframes beanBob{
  from{transform:rotate(var(--r)) translateY(0)}
  to{transform:rotate(calc(var(--r) + 12deg)) translateY(-10px)}
}
@media (max-width:560px){
  .bean{width:calc(var(--s) * .7)}
  .bean.opt{display:none}             /* a narrow hero has no empty margin for six */
}

/* --- REWARDS: the cup and the points ring ----------------------------------
   Static: the ring is closed and the badge shows, the finished picture.
   .live (motion.js) starts the ring empty and the badge hidden, and scroll
   fills one and reveals the other. */
.cupscene{position:relative;width:clamp(230px,28vw,330px);aspect-ratio:1/1;
  margin:-4px auto clamp(30px,2vw + 20px,46px)}
.cupscene picture{display:contents}
.cup-img{position:absolute;left:5%;top:5%;width:90%;height:90%;will-change:transform}
.cup-ring{position:absolute;inset:0;width:100%;height:100%;transform:rotate(-90deg);overflow:visible}
.cup-ring circle{fill:none;stroke-width:1.5}
.cup-ring .track{stroke:rgba(240,230,216,.14)}
.cup-ring .fill{stroke:#e9c48f;stroke-linecap:round;stroke-dasharray:100;stroke-dashoffset:0;
  filter:drop-shadow(0 0 3px rgba(241,220,184,.6))}
.cup-badge{position:absolute;left:50%;bottom:-14px;transform:translateX(-50%);white-space:nowrap;
  padding:7px 14px;border-radius:999px;font-size:13px;font-weight:800;letter-spacing:.02em;
  color:#3a2618;background:linear-gradient(160deg,#f1dcb8,#d9ad78);
  box-shadow:0 10px 24px -10px rgba(0,0,0,.6);
  transition:opacity .45s var(--ease),transform .45s var(--ease)}
.cupscene.live .cup-badge{opacity:0;transform:translate(-50%,10px) scale(.92)}
.cupscene.live.done .cup-badge{opacity:1;transform:translate(-50%,0) scale(1)}

@media (prefers-reduced-motion:reduce){
  .bean picture{animation:none}
}

/* --- REWARDS: the step numbers light up in turn ---------------------------
   Class added by motion.js. Unlit is exactly how they looked before, so no
   script means no change. */
.dark .step .n{transition:background .5s var(--ease),color .5s var(--ease),box-shadow .5s var(--ease),transform .5s var(--ease)}
.dark .step.lit .n{background:linear-gradient(160deg,#f1dcb8,#c99a68);color:#3a2618;
  box-shadow:0 0 0 5px rgba(241,220,184,.16),0 0 22px rgba(241,220,184,.45);transform:scale(1.08)}
