/* Fonts are self-hosted: each page loads fonts/fonts.css (SIL OFL licenses sit next to the .woff2 files). */

/* Palette. Both app themes are taken from the shipped apps, not invented here.
   My Personal Log: the launcher icon is a three-stop ramp, and Cobalt is also the
   in-app brand accent (ProfilePalette.cobalt / fallback).
   Gacha Eats: terracotta is icon and splash only and is never imported into Brand;
   gold is the in-app accent. So terracotta is the outside, gold is the inside. */

:root {
  /* One source of truth per token. light-dark() resolves against the used
     color-scheme, so flipping color-scheme below flips the whole palette.
     Brand values (the dot circle, the logo, the two icon fields) are literal
     on purpose: a launcher icon does not restyle itself for dark mode. */
  color-scheme: light dark;

  --studio-dot: #e4372e;
  --studio-cyan: #c4fafc;

  /* Theme-toggle only. Not brand colours: kept clear of cobalt and terracotta
     so the control never reads as belonging to either app. */
  --sun-tint:   light-dark(#f6d770, #6d5a1c);
  --sun-ink:    light-dark(#4a3a08, #f2d78b);
  --night-tint: light-dark(#c9d2ef, #2b3a72);
  --night-ink:  light-dark(#2a3663, #dbe4ff);

  --log-light: light-dark(#00c9ff, #6fe4ff);
  --log:       light-dark(#2f5ad0, #7c97f2);
  --log-deep:  light-dark(#16306e, #a9bef8);
  --log-soft:  light-dark(#e3eafb, #1b2540);

  --eats:           light-dark(#d9552b, #e8703f);
  --eats-strong:    light-dark(#b4411f, #e8703f);
  --eats-gold:      #e8b923;
  --eats-gold-deep: #c8952f;
  --eats-text:      light-dark(#a8401c, #ef9068);
  --eats-cream:     light-dark(#fbf6ec, #211b12);
  --eats-soft:      light-dark(#fbe3d7, #2f1d13);

  --ink:       light-dark(#1c1b17, #ecebe4);
  --muted:     light-dark(#636058, #9c988e);
  --paper:     light-dark(#fbfaf5, #15161a);
  --surface:   light-dark(#f2efe6, #1e2026);
  --line:      light-dark(#e0dcd2, #2d2f36);
  --footer:    light-dark(#efebe0, #101216);
  --surface-raised: light-dark(#ffffff, #24272e);
  --device-frame:   light-dark(#1d2821, #3a3f47);
  --device-screen:  light-dark(#f2eee2, #1f2229);
  --band:      light-dark(#201f1b, #0f1012);
  --band-ink:  light-dark(#bdb9ae, #adaaa1);
  --band-line: light-dark(#4a473f, #34363c);

  --radius: 22px;
  --shadow-color: light-dark(rgba(34, 43, 38, .1), rgba(0, 0, 0, .5));
  --shadow: 0 22px 60px var(--shadow-color);

  /* Aliases: the older names are still referenced throughout this file and by the
     three product pages. Remapping them here retints every page at once. */
  --green: var(--log);
  --green-soft: var(--log-soft);
  --orange: var(--eats);
  --orange-dark: var(--eats-text);
  --orange-soft: var(--eats-soft);
  --yellow: var(--eats-gold);
  --blue: var(--muted);
  /* Sits on the dark manifesto band, which is dark in BOTH themes, so this must
     stay light always. Deliberately not aliased to --studio-cyan, which inverts. */
  --blue-light: #a8e6ec;
  --dot: var(--studio-dot);
}

/* The toggle writes data-theme onto <html>. Without it the root keeps
   "light dark" and simply follows the operating system. */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark; }

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body { margin: 0; color: var(--ink); background: var(--paper); font-family: "DM Sans", system-ui, sans-serif; line-height: 1.65; }
a { color: inherit; text-decoration: none; }
h1, h2, h3, p { margin-top: 0; }
h1, h2 { letter-spacing: -.045em; line-height: 1.04; }
h1 { font-size: clamp(3.2rem, 7.4vw, 7.3rem); }
h2 { font-size: clamp(2.1rem, 4vw, 4rem); }
h3 { line-height: 1.2; }

.site-header { min-height: 80px; padding: 16px max(24px, calc((100vw - 1240px) / 2)); display: flex; align-items: center; gap: 32px; border-bottom: 1px solid var(--line); background: color-mix(in srgb, var(--paper) 94%, transparent); position: relative; z-index: 10; }
/* The wordmark is the only brand element in the header now that the logo moved to
   the footer, so it is stepped clearly above the nav links rather than sitting level
   with them. */
.brand { display: flex; align-items: center; gap: 11px; font-weight: 700; white-space: nowrap; font-size: 1.28rem; letter-spacing: -.025em; }
/* The brand mark is the actual studio logo (stickman at a standing desk in the cyan
   dot), not a generic dot glyph. One file, referenced by every page. */
/* The logo appears in the footer only. Its circle sits inside a 768 canvas with wide
   margins, so the mark zooms to 158% to crop that circle to the edge of the round frame. */
.brand-mark, .tiny-mark { width: 72px; height: 72px; flex: none; display: inline-block; border-radius: 50%; background: url("assets/logo.svg") center / 158% no-repeat; }
.main-nav { display: flex; justify-content: center; gap: 4px; margin-left: auto; }
.nav-link { padding: 9px 13px; border-radius: 99px; color: var(--muted); font-size: .92rem; font-weight: 600; }
.nav-link:hover, .nav-link.active { color: var(--ink); background: var(--surface); }
.button { display: inline-flex; align-items: center; justify-content: center; gap: 9px; padding: 13px 20px; border-radius: 99px; background: var(--ink); color: var(--paper); font-weight: 700; white-space: nowrap; transition: background-color .18s ease, box-shadow .18s ease; }
.button:hover { background: color-mix(in srgb, var(--ink) 86%, var(--muted)); box-shadow: 0 6px 16px var(--shadow-color); }
.button:active { background: color-mix(in srgb, var(--ink) 74%, var(--muted)); box-shadow: 0 2px 6px var(--shadow-color); transition-duration: .05s; }
.button-log, .button-eats { color: light-dark(#fff, #17181c); }
.button-small { padding: 9px 15px; font-size: .88rem; }
.button-outline { background: transparent; color: var(--ink); border: 1px solid var(--line); }
.button-outline:hover { background: var(--surface); box-shadow: none; }
.button-outline:active { background: var(--line); }
.text-link, .card-link { font-weight: 700; border-bottom: 1px solid currentColor; }
.text-link:hover, .card-link:hover { opacity: .62; }
a:focus-visible, .button:focus-visible { outline: 3px solid var(--green); outline-offset: 3px; }
.theme-home a:focus-visible { outline-color: var(--blue); }
.theme-eats a:focus-visible { outline-color: var(--orange); }

.hero { max-width: 1240px; margin: 0 auto; padding: 100px 24px 112px; }
.eyebrow { color: var(--green); font-size: .76rem; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; }
.hero h1 { margin: 20px 0 28px; }
.hero h1 span, .product-hero h1 span, .page-intro h1 span { font-family: "Fraunces", Georgia, serif; color: var(--green); font-weight: 600; }
/* :not(.eyebrow) matters: these are more specific than .eyebrow, so without it the
   guide and help-card eyebrows lose their accent colour and fall back to body grey. */
.hero-lead, .product-hero-copy > p, .page-intro > p:not(.eyebrow) { max-width: 680px; color: var(--muted); font-size: clamp(1.1rem, 1.7vw, 1.35rem); }
.hero-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 22px; margin-top: 36px; }

.section { max-width: 1240px; margin: 0 auto; padding: 100px 24px; }
.section-heading { display: grid; grid-template-columns: 1.4fr .6fr; gap: 80px; align-items: end; margin-bottom: 48px; }
.section-heading h2 { max-width: 790px; margin-bottom: 0; }
.section-heading > p { color: var(--muted); margin-bottom: 7px; }
/* padding-top exceeds padding-bottom on purpose: the label is all caps, so it has no
   descenders and its ink sits high in the line box. Symmetric padding looks bottom-heavy. */
.pill { display: inline-flex; align-items: center; padding: 9px 11px 7px; border-radius: 99px; margin-bottom: 12px; font-size: .75rem; line-height: 1; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
.pill-log { background: var(--log); color: light-dark(#fff, #17181c); }
.pill-eats { background: var(--eats-strong); color: light-dark(#fff, #17181c); }
.card-link { display: inline-block; margin-top: 22px; }

.manifesto { margin-top: 90px; padding: 120px max(24px, calc((100vw - 1240px) / 2)); background: var(--band); color: #f2f4f1; }
.manifesto > h2 { max-width: 850px; }
.manifesto .eyebrow { color: var(--band-ink); }
.values-grid { margin-top: 70px; display: grid; grid-template-columns: repeat(3, 1fr); gap: 55px; }
.values-grid > div { padding-top: 20px; border-top: 1px solid var(--band-line); }
.values-grid span { color: var(--band-ink); font-size: .75rem; }.values-grid h3 { margin: 20px 0 10px; }.values-grid p { color: var(--band-ink); }

.subnav { display: flex; justify-content: center; align-items: center; gap: 6px; padding: 22px 24px 0; }
.subnav a { padding: 9px 16px; border-radius: 99px; color: var(--muted); font-size: .85rem; font-weight: 600; }
.subnav a:hover { color: var(--ink); background: var(--surface); }
.subnav a.active, .subnav a.active:hover { color: light-dark(#fff, #17181c); background: var(--green); }
.theme-eats .subnav a.active, .theme-eats .subnav a.active:hover { background: var(--eats-strong); color: light-dark(#fff, #17181c); }
.product-hero { max-width: 1240px; min-height: 690px; margin: 0 auto; padding: 60px 24px 100px; display: grid; grid-template-columns: 1.1fr .9fr; gap: 8%; align-items: center; }
.product-hero h1 { margin: 22px 0 28px; font-size: clamp(3.4rem, 7vw, 6.6rem); }
.button-log { background: var(--log); }.button-log:hover { background: color-mix(in srgb, var(--log) 86%, var(--ink)); box-shadow: 0 6px 16px var(--shadow-color); }.button-log:active { background: color-mix(in srgb, var(--log) 74%, var(--ink)); }
.button-eats { background: var(--eats-strong); }.button-eats:hover { background: color-mix(in srgb, var(--eats-strong) 86%, var(--ink)); box-shadow: 0 6px 16px var(--shadow-color); }.button-eats:active { background: color-mix(in srgb, var(--eats-strong) 74%, var(--ink)); }
.theme-eats .eyebrow { color: var(--orange-dark); }.theme-eats .product-hero h1 span, .theme-eats .page-intro h1 span { color: var(--orange); }
.theme-home .eyebrow { color: var(--blue); }.theme-home .hero h1 span { color: var(--blue); }
.theme-home .manifesto .eyebrow, .theme-home .values-grid span { color: var(--blue-light); }
.device-placeholder { width: min(100%, 390px); min-height: 570px; justify-self: center; border: 9px solid var(--device-frame); border-radius: 45px; position: relative; padding: 28px 24px; box-shadow: var(--shadow); overflow: hidden; }
.log-device { background: var(--device-screen); transform: rotate(2deg); }
.device-top { width: 84px; height: 22px; margin: -28px auto 44px; background: var(--device-frame); border-radius: 0 0 14px 14px; }
.device-content small { color: var(--green); font-size: .64rem; font-weight: 700; letter-spacing: .1em; }
.device-content h2 { margin: 17px 0; font-family: "Fraunces", serif; font-size: 2.25rem; }
.device-content > p { color: var(--muted); }
.tag-row { display: flex; gap: 7px; margin: 28px 0; }.tag-row span { padding: 5px 9px; border-radius: 99px; background: var(--green-soft); color: var(--green); font-size: .7rem; }
.entry-card { margin-top: 42px; padding: 20px; background: var(--surface-raised); border-radius: 15px; box-shadow: 0 8px 25px var(--shadow-color); }.entry-card strong { display: block; margin-top: 8px; }
.soft-section { max-width: none; padding-left: max(24px, calc((100vw - 1240px) / 2)); padding-right: max(24px, calc((100vw - 1240px) / 2)); background: var(--surface); }
.feature-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
.feature-grid article { padding: 32px; border-radius: 16px; background: var(--paper); border: 1px solid var(--line); }
.feature-icon { width: 46px; height: 46px; display: grid; place-items: center; margin-bottom: 36px; border-radius: 13px; background: var(--green-soft); color: var(--green); font-size: 1.4rem; }
.theme-eats .feature-icon { background: var(--orange-soft); color: var(--orange-dark); }.feature-grid p { color: var(--muted); }
.quote-section { padding: 130px 24px; text-align: center; }.quote-section p { font-family: "Fraunces", serif; font-size: clamp(2.3rem, 5vw, 5rem); line-height: 1.1; }
.stats-strip { width: min(1192px, calc(100% - 48px)); margin: 40px auto; padding: 45px; display: grid; grid-template-columns: repeat(3, 1fr); background: var(--eats-strong); color: light-dark(#fff, #17181c); border-radius: var(--radius); text-align: center; }
.stats-strip div + div { border-left: 1px solid light-dark(rgba(255,255,255,.4), rgba(23,24,28,.28)); }.stats-strip strong { display: block; font-family: "Fraunces", serif; font-size: 3rem; }.stats-strip span { font-size: .85rem; }
.gacha-machine { width: min(100%, 400px); min-height: 500px; justify-self: center; padding: 28px; border: 9px solid var(--eats-strong); border-radius: 44px 44px 24px 24px; background: var(--eats); box-shadow: var(--shadow); position: relative; transform: rotate(-2deg); }
.machine-top { display: flex; justify-content: center; gap: 9px; margin-bottom: 24px; }.machine-top span { width: 14px; height: 14px; background: var(--yellow); border-radius: 50%; }
.machine-window { min-height: 285px; padding: 35px 20px; text-align: center; border: 7px solid var(--eats-strong); border-radius: 24px; background: var(--eats-cream); }.machine-window p { color: var(--orange-dark); font-size: .7rem; font-weight: 700; letter-spacing: .14em; }.machine-window strong { display: block; font-size: 4rem; }.machine-window h2 { margin: 5px 0; font-family: "Fraunces", serif; font-size: 2.3rem; }.machine-window span { color: var(--muted); font-size: .8rem; }
.machine-button { width: 150px; margin: 26px auto 0; padding: 12px; text-align: center; background: var(--eats-gold); color: #231a09; border: 4px solid var(--eats-strong); border-radius: 99px; font-size: .75rem; font-weight: 700; }
.gacha-machine .device-caption { color: light-dark(#7b2c16, #ffd9c6); bottom: 7px; }

/* Hero pull clip (overview page): the phone frame that replaced the mockup. */
.eats-hero .hero-shot { width: min(100%, 300px); justify-self: center; overflow: hidden; border: 9px solid var(--eats-strong); border-radius: 40px; background: var(--eats-cream); box-shadow: var(--shadow); transform: rotate(2deg); }
.eats-hero .hero-shot video { display: block; width: 100%; height: auto; aspect-ratio: 1080 / 2400; }
.eats-hero .hero-shot .shot-dark { display: none; }
:root[data-theme="dark"] .eats-hero .hero-shot .shot-light { display: none; }
:root[data-theme="dark"] .eats-hero .hero-shot .shot-dark { display: block; }

.page-intro { max-width: 1080px; margin: 0 auto; padding: 84px 24px 90px; text-align: center; }.page-intro h1 { margin: 20px 0 30px; }.page-intro > p { margin-left: auto; margin-right: auto; }
.guide-list { max-width: 1050px; margin: 0 auto; padding: 0 24px 100px; }
.guide-step { display: grid; grid-template-columns: 90px 1fr; gap: 45px; padding: 70px 0; border-top: 1px solid var(--line); }
.step-number { font-family: "Fraunces", serif; color: var(--green); font-size: 2rem; }.eats-guide .step-number { color: var(--orange); }
.step-kicker { margin-bottom: 5px; color: var(--muted); font-size: .72rem; font-weight: 700; letter-spacing: .13em; text-transform: uppercase; }.guide-step h2 { font-size: clamp(2rem, 3.5vw, 3.4rem); }.guide-step > div > p:not(.step-kicker) { max-width: 650px; color: var(--muted); }
.mini-placeholder, .search-demo, .pool-demo, .draw-demo, .favorite-demo { max-width: 560px; margin-top: 30px; padding: 24px; border-radius: 15px; background: var(--surface-raised); border: 1px solid var(--line); box-shadow: 0 12px 30px var(--shadow-color); }
.mini-placeholder * { display: block; }.mini-placeholder small { color: var(--green); font-size: .65rem; }.mini-placeholder strong { margin: 10px 0 15px; }.mini-placeholder span, .search-demo span { color: var(--muted); }
.chip-demo, .food-options { max-width: 600px; display: flex; flex-wrap: wrap; gap: 9px; margin-top: 30px; }.chip-demo span, .food-options span { padding: 9px 14px; border-radius: 99px; background: var(--green-soft); color: var(--green); font-size: .85rem; }.food-options span { background: var(--orange-soft); color: var(--orange-dark); }
.pool-demo, .favorite-demo { display: flex; justify-content: space-between; }.pool-demo small { color: var(--orange-dark); font-weight: 700; }.draw-demo { display: flex; align-items: center; gap: 15px; color: light-dark(#fff, #17181c); background: var(--eats-strong); }.draw-demo strong { font-size: 2rem; }.favorite-demo { color: var(--eats-text); background: var(--eats-soft); }

.policy-layout { max-width: 1120px; margin: 0 auto; padding: 84px 24px 140px; display: grid; grid-template-columns: 270px 1fr; gap: 95px; align-items: start; }
.policy-layout aside { position: sticky; top: 30px; }.policy-layout h1 { margin: 15px 0; font-size: 3.5rem; }.policy-date { color: var(--muted); font-size: .85rem; }
/* The contents list (PolicyIndex.astro). On desktop it's a plain list with a rail
   down its left edge, and the section being read (policy-index.js) lights its
   stretch of the rail. It only scrolls sideways in the phone bar, at the end of
   this file, so here the .hs viewport must not clip: focus rings stick out past it. */
.policy-index { --index-accent: var(--log); margin-top: 40px; }.theme-eats .policy-index { --index-accent: var(--orange); }.theme-home .policy-index { --index-accent: var(--ink); }
.policy-index .hs-viewport { overflow: visible; }.policy-index .hs-content { display: grid; }
.policy-index a { padding: 4px 0 4px 14px; border-left: 2px solid var(--line); color: var(--muted); font-size: .9rem; transition: color .16s ease, border-color .16s ease; }.policy-index a:hover, .policy-index a.is-current { color: var(--ink); }.policy-index a.is-current { border-left-color: var(--index-accent); }
.policy-content section { padding: 38px 0; border-bottom: 1px solid var(--line); }.policy-content h2 { font-size: 2rem; letter-spacing: -.025em; }.policy-content p, .policy-content li { color: var(--muted); }.policy-content a { color: var(--green); text-decoration: underline; }.theme-eats .policy-content a { color: var(--orange-dark); }
.draft-notice { padding: 26px; border-radius: 14px; background: light-dark(#fff2c6, #33290b); border: 1px solid light-dark(#efd77c, #6b5a1c); }.draft-notice p { margin: 6px 0 0; color: light-dark(#675a2f, #e8d9a6); }

.site-footer { padding: 70px max(24px, calc((100vw - 1240px) / 2)) 24px; display: grid; grid-template-columns: 1fr auto; gap: 55px; background: var(--footer); border-top: 1px solid var(--line); }
.footer-brand { display: flex; align-items: center; gap: 18px; }.footer-brand p:not(.footer-eyebrow) { margin: 4px 0; color: var(--muted); font-size: .8rem; }
/* Footer only, and deliberately NOT .eyebrow. That class is app-tinted
   (.theme-eats .eyebrow, .theme-home .eyebrow), so reusing it would paint the
   footer terracotta on the eats pages and grey on the home page. The footer is
   shared chrome and has to look identical on all nine. The :not() above is the
   same specificity fix already used for .page-intro > p, since .footer-brand p
   is (0,1,1) and would otherwise beat this rule silently. */
.footer-eyebrow { margin: 0 0 3px; color: var(--muted); font-size: .66rem; font-weight: 700; letter-spacing: .15em; text-transform: uppercase; opacity: .85; }.footer-links { display: grid; gap: 7px; font-size: .86rem; justify-self: end; text-align: right; }.footer-bottom { grid-column: 1 / -1; margin: 40px 0 0; padding-top: 18px; border-top: 1px solid var(--line); color: var(--muted); font-size: .75rem; }
.footer-bottom a { text-decoration: underline; }

/* Expanded app-page sections: contrast manifesto, cross-promo, guide lists, tips, FAQ */
.manifesto > p { max-width: 780px; color: var(--band-ink); font-size: 1.08rem; }
.contrast-grid { margin-top: 60px; display: grid; grid-template-columns: repeat(2, 1fr); gap: 34px 55px; }
.contrast-grid > div { padding-top: 20px; border-top: 1px solid var(--band-line); color: var(--band-ink); }
.contrast-grid strong { display: block; color: #f2f4f1; margin-bottom: 6px; }
.guide-step ul { max-width: 650px; margin: 0 0 16px; padding-left: 22px; color: var(--muted); }
.guide-step ul li { margin: 7px 0; }
.step-tip { max-width: 650px; font-size: .92rem; font-weight: 600; color: var(--green); }
.eats-guide .step-tip { color: var(--orange-dark); }
.faq-section { max-width: 900px; margin: 0 auto; padding: 80px 24px 110px; }
.faq-section h2 { margin: 14px 0 26px; }
/* Closes the section on prose so the page does not run from a collapsed accordion
   row straight into the footer. */
.faq-outro { max-width: 66ch; margin: 34px 0 0; color: var(--muted); }
.faq-outro a { color: var(--green); text-decoration: underline; text-underline-offset: 3px; }
.theme-eats .faq-outro a { color: var(--eats-text); }
.faq-item { border-top: 1px solid var(--line); }
.faq-item:last-of-type { border-bottom: 1px solid var(--line); }
.faq-item summary { padding: 22px 0; font-weight: 700; font-size: 1.08rem; cursor: pointer; list-style: none; display: flex; justify-content: space-between; align-items: center; gap: 20px; }
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after { content: "+"; font-family: "Fraunces", serif; font-size: 1.5rem; color: var(--muted); flex-shrink: 0; }
.faq-item[open] summary::after { content: "−"; }
.faq-item p { margin: 0 0 26px; max-width: 700px; color: var(--muted); }

@media (max-width: 900px) {
  .site-header { flex-wrap: wrap; gap: 10px 16px; }.main-nav { order: 3; width: 100%; justify-content: flex-start; overflow-x: auto; }
  .section-heading { grid-template-columns: 1fr; gap: 20px; }.values-grid, .feature-grid { grid-template-columns: 1fr; }.contrast-grid { grid-template-columns: 1fr; gap: 24px; }
  .site-footer { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 600px) {
  h1 { font-size: 3rem; }.hero, .section, .product-hero { padding-top: 68px; padding-bottom: 72px; }.brand > span:last-child { font-size: 1.08rem; }.nav-link { padding-left: 9px; padding-right: 9px; font-size: .82rem; }
  .subnav { gap: 2px; }.subnav a { padding: 8px 12px; }.values-grid { gap: 24px; }
  .stats-strip { grid-template-columns: 1fr; }.stats-strip div + div { border-left: 0; border-top: 1px solid rgba(255,255,255,.4); padding-top: 20px; margin-top: 20px; }
  .guide-step { grid-template-columns: 1fr; gap: 15px; padding: 50px 0; }
  .site-footer { grid-template-columns: 1fr; }
  .footer-links { justify-self: start; text-align: left; }.footer-bottom { margin-top: 10px; }.footer-links { margin: 10px 0; }
}

@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } * { transition: none !important; } }

/* ---- Home page: the dot hero, app cards, claims ---------------------------- */

.dot-hero { max-width: 1240px; margin: 0 auto; padding: 88px 24px 96px; display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(280px, .75fr); gap: 7vw; align-items: center; }
.dot-hero h1 { margin: 18px 0 10px; font-size: clamp(2.5rem, 5vw, 4.6rem); }
.dot-hero .turn { display: block; margin: 0 0 26px; color: var(--muted); font-size: clamp(2.5rem, 5vw, 4.6rem); font-weight: 400; letter-spacing: -.045em; line-height: 1.04; }
.dot-hero .hero-lead { max-width: 46ch; }
.theme-home .eyebrow { color: var(--muted); }

/* The circle is the logo blown up, so it keeps the logo's light-mode colours in both
   themes, the same way the app icon fields do. Everything inside is literal on purpose. */
.dot-circle { width: min(100%, 420px); justify-self: center; aspect-ratio: 1; border-radius: 50%; background: #c4fafc; display: grid; place-items: center; }
.dot-circle svg { width: 86%; height: auto; }
.dot-circle .stroke { fill: none; stroke: #1c1b17; stroke-width: 3.4; stroke-linecap: round; }
.dot-circle .fill-paper { fill: #fbfaf5; stroke: #1c1b17; stroke-width: 3.4; }
.dot-circle .fill-screen { fill: #eafaf6; stroke: #1c1b17; stroke-width: 3.4; }
.dot-circle .mug { fill: #e4372e; stroke: #1c1b17; stroke-width: 3.4; }

#mugArm { animation: armSip 4.4s ease-in-out infinite; }
#mugCup { animation: mugSip 4.4s ease-in-out infinite; }
@keyframes armSip { 0%, 40% { d: path("M58,92 Q66,98 73,102"); } 55%, 72% { d: path("M58,92 Q64,86 63,78"); } 88%, 100% { d: path("M58,92 Q66,98 73,102"); } }
@keyframes mugSip { 0%, 40% { transform: translate(0, 0); } 55%, 72% { transform: translate(-13px, -27px); } 88%, 100% { transform: translate(0, 0); } }
@media (prefers-reduced-motion: reduce) { #mugArm, #mugCup { animation: none; } }

.app-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 26px; }
.app-card { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--paper); display: flex; flex-direction: column; transition: box-shadow .2s ease, transform .2s ease; }
.app-card:hover { box-shadow: var(--shadow); transform: translateY(-3px); }
/* Both icons carry their own background, baked to the exact hex of the field behind
   them (cobalt disc on cobalt, terracotta tile on terracotta), so the artwork appears
   to float. Sized up because the art sits in the centre ~60% of each canvas. */
.app-field { height: 200px; display: grid; place-items: center; overflow: hidden; }
.app-field img, .app-field svg { width: 176px; height: 176px; display: block; }
/* Literal, not themed: these fields stand in for the launcher icons, and a launcher
   icon's background does not change with the system theme. Cobalt is the adaptive
   icon background layer; terracotta is app.json's adaptiveIcon.backgroundColor. */
.app-field-log { background: #2f5ad0; }
.app-field-eats { background: #d9552b; }
.app-body { padding: 30px 32px 34px; }
.app-body h3 { margin: 8px 0 12px; font-family: "Fraunces", Georgia, serif; font-size: clamp(1.9rem, 2.6vw, 2.6rem); }
.app-body > p { color: var(--muted); margin-bottom: 20px; }
.app-card-log h3 { color: var(--log-deep); }
.app-card-log .card-link { color: var(--log); }
.app-card-eats h3 { color: var(--eats-text); }
.app-card-eats .card-link { color: var(--eats-text); }

/* ---- Theme toggle ---------------------------------------------------------- */
/* Hidden until theme.js adds .js to <html>: the control does nothing without
   JavaScript, so without JavaScript it should not be there to click. */
.theme-toggle { display: none; }
:root.js .theme-toggle { display: inline-grid; place-items: center; width: 38px; height: 38px; padding: 0; margin-left: 4px; border: 1px solid var(--line); border-radius: 50%; background: transparent; color: var(--muted); cursor: pointer; transition: color .18s ease, background-color .18s ease, border-color .18s ease; }
/* Diagonal split square: sun in the top-left triangle, moon in the bottom-right.
   The half you are currently in is the one that is filled, both in its icon and in
   its background tint, so the control states the current mode and the alternative
   in one shape. Sun and night tints are UI colours only, deliberately outside both
   app palettes so the control never reads as branding. */
.mode-glyph { width: 100%; height: 100%; display: block; }
.glyph-tri { fill: transparent; transition: fill .3s ease; }
:root[data-theme="light"] .glyph-tri-sun  { fill: var(--sun-tint); }
:root[data-theme="dark"]  .glyph-tri-moon { fill: var(--night-tint); }

.glyph-split { stroke: var(--line); stroke-width: 1.1; fill: none; }
.glyph-frame { stroke: var(--line); stroke-width: 1.2; fill: none; transition: stroke .18s ease; }
.theme-toggle:hover .glyph-frame { stroke: var(--muted); }

.glyph-sun, .glyph-moon { color: var(--muted); transition: color .3s ease; }
.sun-core, .glyph-moon { fill: none; stroke: currentColor; stroke-width: 1.35; stroke-linejoin: round; transition: fill .3s ease; }
.sun-rays { fill: none; stroke: currentColor; stroke-width: 1.15; stroke-linecap: round; }
:root[data-theme="light"] .glyph-sun  { color: var(--sun-ink); }
:root[data-theme="light"] .sun-core   { fill: currentColor; }
:root[data-theme="dark"]  .glyph-moon { color: var(--night-ink); fill: currentColor; }

/* ---- Mobile nav ------------------------------------------------------------ */
/* Same contract as the theme toggle: the button only exists once JS can drive it,
   so without JS the nav simply stays visible as a plain row. */
.nav-toggle { display: none; }
.nav-toggle svg { width: 22px; height: 22px; display: block; }

@media (max-width: 900px) {
  :root.js .nav-toggle { display: inline-grid; place-items: center; width: 44px; height: 44px; padding: 0; border: 1px solid var(--line); border-radius: 12px; background: transparent; color: var(--ink); cursor: pointer; }
  :root.js .nav-toggle:hover { background: var(--surface); border-color: var(--muted); }
  :root.js .nav-toggle .icon-close { display: none; }
  :root.js .nav-toggle[aria-expanded="true"] .icon-open { display: none; }
  :root.js .nav-toggle[aria-expanded="true"] .icon-close { display: block; }

  /* auto margin must sit on the same specificity as the base rule, which sets
     margin-left: 4px, or it loses and the controls stay stuck to the wordmark. */
  :root.js .theme-toggle { margin-left: auto; width: 44px; height: 44px; }
  :root.js .main-nav { display: none; }
  :root.js .main-nav.is-open { display: flex; flex-direction: column; gap: 2px; overflow-x: visible; padding-bottom: 10px; }
  :root.js .main-nav.is-open .nav-link { padding: 11px 13px; font-size: .95rem; }
}

/* ---- Footer nav ------------------------------------------------------------ */
.footer-nav { display: grid; grid-template-columns: repeat(2, minmax(0, auto)); gap: 20px 54px; justify-self: end; }
.footer-nav h2 { margin: 0 0 12px; padding-bottom: 7px; border-bottom: 1px solid var(--line); font-size: .72rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.footer-nav ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 7px; }
.footer-nav a { font-size: .86rem; }
.footer-nav a:hover { text-decoration: underline; text-underline-offset: 3px; }

@media (max-width: 600px) {
  .footer-nav { justify-self: start; gap: 20px 36px; }
}

/* ---- Home page responsive -------------------------------------------------- */
/* Must live after the .dot-hero base rule above: same specificity, so source order
   decides. Kept here rather than in the shared breakpoint block for that reason. */
@media (max-width: 900px) {
  .dot-hero { grid-template-columns: 1fr; gap: 40px; padding: 56px 24px 64px; }
  .dot-circle { width: min(74%, 300px); justify-self: start; }
  .dot-hero h1, .dot-hero .turn { font-size: clamp(2.1rem, 8.4vw, 3rem); }
  .dot-hero .hero-lead { max-width: none; }
  .app-field { height: 156px; }
  .app-field img, .app-field svg { width: 128px; height: 128px; }
}

/* ---- Type corrections ------------------------------------------------------ */
/* Fraunces is self-hosted as a single 600 cut. Headings default to 700, so every
   Fraunces heading was being rendered as a SYNTHETIC bold: the browser smears the
   600 outlines, which destroys exactly the stroke contrast the face is chosen for.
   Asking for the weight that actually exists fixes it. DM Sans ships 400-700 here,
   so the one font-weight: 800 in the file was synthesised too and is now 700. */
.note-text, .product-number, .product-card h3, .mock-title, .mock-food,
.device-content h2, .quote-section p, .stats-strip strong, .machine-window h2,
.step-number, .app-body h3, .faq-item summary::after { font-weight: 600; }

/* The wordmark is the one place the two typefaces meet. */
.brand { font-family: "Fraunces", Georgia, serif; font-weight: 600; letter-spacing: -.01em; }

/* ---- Claim band ------------------------------------------------------------ */
/* A property, not a feature. Pulled out of the feature grid because "what it is
   built like" does not belong in a list of "what it is for". */
/* margin-top matters: the section above is tinted and its padding sits INSIDE that
   tint, so without it the band butts straight onto the block above while keeping 90px
   below. Equal margins here; the bottom one collapses with .manifesto's margin-top. */
.claim-band { width: min(1192px, calc(100% - 48px)); margin: 90px auto; padding: clamp(40px, 5vw, 64px) clamp(26px, 5vw, 72px); border-radius: var(--radius); background: var(--log-soft); }
.claim-band h2 { max-width: 22ch; margin-bottom: 22px; font-size: clamp(1.8rem, 3.2vw, 2.9rem); color: var(--log-deep); }
/* Cool greys, not --muted. The page's body colour is warm and the band is cool, and
   warm text on a cool ground reads as slightly dirty. */
.claim-band p { max-width: 66ch; margin-bottom: 14px; color: light-dark(#4a5570, #b3bdd6); }
.claim-band p:last-child { margin-bottom: 0; }
.claim-band .eyebrow { color: light-dark(#4e5872, #9fadcc); }
/* Emphasis by hue, not by brightness. Near-white bold on a dark band just reads louder;
   the accent reads as a different kind of word. */
.claim-band strong { color: var(--log-deep); font-weight: 700; }
.theme-eats .claim-band { background: var(--eats-soft); }
.theme-eats .claim-band h2, .theme-eats .claim-band strong { color: var(--eats-text); }
.theme-eats .claim-band p { color: light-dark(#6b5340, #d8c3ac); }
.theme-eats .claim-band .eyebrow { color: light-dark(#8a6a4a, #c0a891); }

/* ---- App posts (long-form Markdown notes) ---------------------------------- */
/* Rendered from src/pages/personal-log-*.md through layouts/Post.astro. The
   Markdown body is plain semantic HTML (h1/h2/p/pre/table/blockquote), so these
   rules carry all of its styling. Syntax highlighting is OFF in astro.config
   (Shiki's inline styles would break the CSP), so code is themed by class only. */
.post-main { padding: 0 24px; }
.post { max-width: 760px; margin: 0 auto; padding: 40px 0 110px; }
.post .eyebrow { display: block; margin-bottom: 18px; color: var(--green); }
.post h1 { font-size: clamp(2.4rem, 5vw, 3.6rem); margin: 0 0 28px; }
.post h2 { font-size: clamp(1.55rem, 3vw, 2.2rem); margin: 66px 0 20px; }
.post h3 { font-size: 1.2rem; margin: 40px 0 12px; }
.post p { color: var(--ink); margin: 0 0 20px; }
/* The intro paragraph sits right after the H1 (the eyebrow is the first <p>, so
   :first-of-type would wrongly target it). */
.post h1 + p { font-size: 1.14rem; color: var(--muted); }
.post a { color: var(--green); text-decoration: underline; text-underline-offset: 3px; }
.post strong { font-weight: 700; }
.post ul, .post ol { margin: 0 0 20px; padding-left: 24px; color: var(--ink); }
.post li { margin: 8px 0; }
.post li::marker { color: var(--muted); }
/* Section dividers (Markdown `---`). A quiet full-measure rule with air on both
   sides, not the heavy hero-band look. */
.post hr { border: 0; height: 1px; background: var(--line); margin: 56px 0; }
.post blockquote { margin: 24px 0; padding: 4px 0 4px 22px; border-left: 3px solid var(--green); color: var(--muted); font-style: italic; }
.post blockquote p { color: inherit; }

/* Inline and block code. A system monospace stack, so nothing new is fetched
   (font-src 'self' governs @font-face only, not platform fonts). */
.post code { font-family: ui-monospace, "Cascadia Code", "SF Mono", Consolas, "Liberation Mono", monospace; font-size: .88em; background: var(--surface); padding: 2px 6px; border-radius: 6px; }
.post pre { margin: 24px 0; padding: 18px 20px; background: var(--surface); border: 1px solid var(--line); border-radius: 14px; overflow-x: auto; }
.post pre code { background: none; padding: 0; font-size: .82rem; line-height: 1.6; color: var(--ink); white-space: pre; }

.post table { width: 100%; border-collapse: collapse; margin: 24px 0; font-size: .94rem; }
.post th, .post td { padding: 11px 14px; border: 1px solid var(--line); text-align: left; vertical-align: top; }
.post thead th { background: var(--surface); font-weight: 700; }
.post td { color: var(--muted); }

.post-back { margin: 70px 0 0; padding-top: 26px; border-top: 1px solid var(--line); }
.post-back a { color: var(--green); font-weight: 700; }
.post-back a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* ---- "Posts related to the app" card row (personal-log-how-to) -------------- */
.posts-section .section-heading { margin-bottom: 40px; }
.post-card-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
.post-card { display: flex; flex-direction: column; padding: 30px 30px 28px; border: 1px solid var(--line); border-radius: 16px; background: var(--paper); transition: box-shadow .2s ease, transform .2s ease; }
.post-card:hover { box-shadow: var(--shadow); transform: translateY(-3px); }
.post-card-kicker { color: var(--green); font-size: .7rem; font-weight: 700; letter-spacing: .13em; text-transform: uppercase; }
.post-card h3 { margin: 12px 0 10px; font-family: "Fraunces", Georgia, serif; font-weight: 600; font-size: 1.5rem; color: var(--log-deep); }
.post-card p { flex: 1; margin: 0 0 20px; color: var(--muted); font-size: .96rem; }
.post-card-link { color: var(--log); font-weight: 700; font-size: .92rem; }
.post-card:hover .post-card-link { text-decoration: underline; text-underline-offset: 3px; }

@media (max-width: 900px) {
  .post-card-grid { grid-template-columns: 1fr; }
  .post h2 { margin-top: 52px; }
}

/* ==== My Personal Log how-to: screenshots, voyage rail, settings spotlight ==== */
/* Added for the illustrated guide. All cobalt via --log; theme-aware. */

/* ---- Light/dark screenshot pairs ------------------------------------------- */
/* Two <img>s per shot; CSS shows the one matching <html data-theme>. No-JS
   falls back to light (data-theme absent -> dark stays hidden, light shows). */
/* Specific enough (0,2,0) to beat the `.app-shot img`/`.shot-frame img`
   display:block sizing rules below (0,1,1); the dark reveals match it. */
.app-shot .shot-dark, .shot-frame .shot-dark { display: none; }
:root[data-theme="dark"] .app-shot .shot-light,
:root[data-theme="dark"] .shot-frame .shot-light { display: none; }
:root[data-theme="dark"] .app-shot .shot-dark,
:root[data-theme="dark"] .shot-frame .shot-dark { display: block; }
.app-shot img, .shot-frame img, .shot-frame video { aspect-ratio: 1080 / 2400; }

/* Borderless in-step shot: rounded, soft shadow, hairline so a light screenshot
   never dissolves into a light page. */
.app-shot { margin: 0; width: min(100%, 300px); }
.app-shot img { width: 100%; height: auto; display: block; border-radius: 22px;
  border: 1px solid var(--line); box-shadow: var(--shadow); background: var(--device-screen); }

/* Framed hero: the screenshot inside a device body with a notch. */
/* Radius kept modest so the rounded top corners don't clip the status-bar
   clock sitting in the screenshot's top-left. */
.shot-frame { position: relative; width: min(100%, 296px); margin: 0 auto;
  border: 10px solid var(--device-frame); border-radius: 30px; overflow: hidden;
  box-shadow: var(--shadow); background: var(--device-frame); }
.shot-frame::before { content: ""; position: absolute; top: 0; left: 50%;
  transform: translateX(-50%); width: 84px; height: 18px; background: var(--device-frame);
  border-radius: 0 0 13px 13px; z-index: 2; }
.shot-frame img, .shot-frame video { width: 100%; height: auto; display: block; }
.intro-hero { margin: 44px auto 0; }
/* Overview hero: same framed shot, tilted to echo the old placeholder. */
.hero-shot { justify-self: center; transform: rotate(2deg); }

/* ---- The voyage: a paper boat sails the water rail 01->05, app icon at 06 --- */
.guide-list.voyage { max-width: 1140px; position: relative; }
.voyage .guide-step {
  grid-template-columns: 56px minmax(0, 1fr) minmax(0, 300px);
  grid-template-areas: "rail body shot";
  gap: 40px; align-items: start; padding: 46px 0; border-top: 0;
}
/* Pull the first step up under the page heading — the intro already carries
   generous bottom padding, so the full step padding on top of it reads as a gap. */
.voyage .guide-step:first-of-type { padding-top: 12px; }
.voyage-rail { grid-area: rail; position: relative; align-self: stretch; }
.step-body   { grid-area: body; }
.guide-step .app-shot { grid-area: shot; justify-self: end; }

/* The river runs down the rail column, drawn entirely by log-voyage.js so it can
   track the boat: a calm base track plus a vivid "sailed" fill that grows behind
   the boat and recedes when you scroll back up. No JS = no river; the numbers
   live in the headings, so nothing content-bearing is lost. */
.voyage-track, .voyage-fill { position: absolute; z-index: 0; width: 46px;
  border-radius: 24px; transform: translateX(-50%); pointer-events: none; }
.voyage-track { background: color-mix(in srgb, var(--log-light) 28%, transparent); }
.voyage-fill  { z-index: 1; background: color-mix(in srgb, var(--log-light) 62%, transparent); }

/* Destination marker: the app icon (its own circular mark, transparent around). */
.voyage-dest { display: block; width: 54px; height: 54px; margin: 4px auto 0; position: relative; z-index: 2; }
.voyage-dest img { width: 100%; height: 100%; display: block; border-radius: 50%;
  box-shadow: 0 6px 18px color-mix(in srgb, var(--log) 34%, transparent); }

/* Step number, moved off the rail into the heading block so the boat never covers it. */
.step-n { display: block; font-family: "Fraunces", Georgia, serif; font-weight: 600;
  font-size: 2rem; line-height: 1; color: var(--log); margin-bottom: 8px; }

/* The paper boat. It lives on the voyage box (not a step) and log-voyage.js
   drives its left/top so it sails the rail as you scroll; the CSS values are a
   no-JS fallback near the first node. Centring lives on the outer span so it
   survives whether or not the bob animation runs (reduced-motion). */
/* Blue boat (cobalt fill); it reads against the lighter river thanks to the dark
   outline baked into the SVG. */
.voyage-ship { position: absolute; left: 48px; top: 6px; z-index: 3;
  width: 50px; color: var(--log);
  transform: translateX(-50%); pointer-events: none; }
.voyage-ship svg { width: 100%; height: auto; display: block; transform-origin: 50% 80%;
  filter: drop-shadow(0 2px 3px color-mix(in srgb, var(--log-deep) 45%, transparent));
  animation: boatBob 4s ease-in-out infinite; }
@keyframes boatBob { 0%,100% { transform: translateY(0) rotate(-3deg); }
  50% { transform: translateY(-3px) rotate(3deg); } }
@media (prefers-reduced-motion: reduce) { .voyage-ship svg { animation: none; } }

/* The voyage (river + boat + destination) is a desktop flourish. On narrow
   screens it's dropped entirely and steps collapse to a single column; the step
   numbers still live in the headings, so nothing is lost. */
@media (max-width: 900px) {
  .voyage .guide-step {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "body" "shot";
    gap: 6px;
  }
  .guide-step .app-shot { justify-self: start; margin-top: 22px; }
  .voyage-rail, .voyage-ship, .voyage-track, .voyage-fill { display: none; }
}

/* ---- Settings spotlight: click a setting, its explanation lights up --------- */
/* Left column mirrors the app's real Settings screen — uppercase section labels
   over rounded cards of hairline-divided rows; right column is the explanation.
   align-items: stretch keeps the two columns exactly the same height. */
.spotlight { display: grid; grid-template-columns: minmax(0, 358px) minmax(0, 1fr);
  gap: 30px; align-items: stretch; }
/* A plain scroll column — the cards inside carry the chrome, like the app's
   scaffold with floating section cards. */
.spot-list { max-height: 440px; overflow-y: auto; padding-right: 4px;
  scrollbar-width: thin; scrollbar-color: transparent transparent;
  --fade-top: 0px; --fade-bottom: 0px;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--fade-top),
    #000 calc(100% - var(--fade-bottom)), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 var(--fade-top),
    #000 calc(100% - var(--fade-bottom)), transparent 100%); }
.spot-list:hover { scrollbar-color: color-mix(in srgb, var(--muted) 40%, transparent) transparent; }
.spot-group { margin: 0; padding: 0 4px 7px; color: var(--muted); font-size: .66rem;
  font-weight: 700; letter-spacing: .13em; text-transform: uppercase; }
.spot-group:not(:first-of-type) { padding-top: 18px; }
.spot-card { background: var(--surface-raised); border: 1px solid var(--line);
  border-radius: 16px; box-shadow: 0 8px 24px color-mix(in srgb, var(--shadow-color) 60%, transparent);
  overflow: hidden; }
.spot-row { position: relative; display: flex; align-items: center; gap: 13px;
  padding: 13px 15px; cursor: pointer; transition: background .16s ease; }
.spot-row + .spot-row { border-top: 1px solid var(--line); }
.spot-row input { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
.spot-row:hover { background: var(--surface); }
.spot-ic { flex-shrink: 0; width: 22px; height: 22px; color: var(--muted); display: grid; place-items: center; }
.spot-ic svg { width: 20px; height: 20px; display: block; }
.spot-main { flex: 1 1 auto; min-width: 0; }
.spot-name { font-weight: 600; color: var(--ink); }
.spot-trail { display: flex; align-items: center; gap: 7px; margin-left: auto; flex-shrink: 0; }
.spot-val { color: var(--muted); font-size: .85rem; }
.spot-chev { color: var(--muted); font-size: 1.2rem; line-height: 1;
  transition: transform .16s ease, color .16s ease; }
/* Read-only mimic of the app's switch: shows each toggle's default state. */
.spot-toggle { position: relative; width: 34px; height: 20px; border-radius: 99px;
  background: color-mix(in srgb, var(--muted) 45%, transparent); transition: background .16s ease; }
.spot-toggle::after { content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px;
  border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.25);
  transition: transform .16s ease; }
.spot-toggle.is-on { background: var(--log); }
.spot-toggle.is-on::after { transform: translateX(14px); }
.spot-row:has(input:checked) { background: var(--log-soft); }
.spot-row:has(input:checked) .spot-name,
.spot-row:has(input:checked) .spot-ic,
.spot-row:has(input:checked) .spot-chev { color: var(--log); }
.spot-row:has(input:checked) .spot-chev { transform: translateX(3px); }

.spot-stage { position: relative; min-height: 0;
  padding: 30px 32px; border-radius: 22px; background: var(--log-soft);
  border: 1px solid color-mix(in srgb, var(--log) 22%, transparent);
  overflow-y: auto; scrollbar-width: thin; scrollbar-color: transparent transparent; }
.spot-stage:hover { scrollbar-color: color-mix(in srgb, var(--log) 38%, transparent) transparent; }
/* Back control for the mobile slide — hidden on the desktop two-column layout. */
.spot-back { display: none; align-items: center; gap: 5px; margin: -6px 0 16px -8px;
  padding: 7px 11px; border: 0; border-radius: 10px; background: none; cursor: pointer;
  color: var(--log-deep); font: inherit; font-weight: 700; font-size: .92rem; }
.spot-back > span { font-size: 1.15rem; line-height: 1; }
.spot-back:hover { background: color-mix(in srgb, var(--log) 13%, transparent); }
.spot-back:focus-visible { outline: 2px solid var(--log); outline-offset: 2px; }
.spot-panel { display: none; animation: spotFade .25s ease; }
.spot-panel h3 { margin: 0 0 12px; font-family: "Fraunces", Georgia, serif; font-weight: 600;
  font-size: 1.6rem; color: var(--log-deep); }
.spot-panel p { margin: 0 0 12px; color: light-dark(#3f4a68, #b7c2dc); }
.spot-panel p:last-child { margin-bottom: 0; }
.spot-where { display: inline-block; margin-bottom: 14px; padding: 4px 11px;
  border-radius: 99px; background: color-mix(in srgb, var(--log) 15%, transparent);
  color: var(--log-deep); font-size: .72rem; font-weight: 700; letter-spacing: .04em; }
@keyframes spotFade { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .spot-panel { animation: none; } }

/* :has() drives which panel shows. One line per row id. */
.spotlight:has(#set-theme:checked)     .p-theme     { display: block; }
.spotlight:has(#set-taskview:checked)  .p-taskview  { display: block; }
.spotlight:has(#set-cutoff:checked)    .p-cutoff    { display: block; }
.spotlight:has(#set-confirm:checked)   .p-confirm   { display: block; }
.spotlight:has(#set-undo:checked)      .p-undo      { display: block; }
.spotlight:has(#set-haptics:checked)   .p-haptics   { display: block; }
.spotlight:has(#set-autoarch:checked)  .p-autoarch  { display: block; }
.spotlight:has(#set-remind:checked)    .p-remind    { display: block; }
.spotlight:has(#set-htasks:checked)    .p-htasks    { display: block; }
.spotlight:has(#set-hevents:checked)   .p-hevents   { display: block; }
.spotlight:has(#set-hnotes:checked)    .p-hnotes    { display: block; }
.spotlight:has(#set-hprofiles:checked) .p-hprofiles { display: block; }
.spotlight:has(#set-profiles:checked)  .p-profiles  { display: block; }
.spotlight:has(#set-clears:checked)    .p-clears    { display: block; }
.spotlight:has(#set-archive:checked)   .p-archive   { display: block; }
.spotlight:has(#set-export:checked)    .p-export    { display: block; }
.spotlight:has(#set-import:checked)    .p-import    { display: block; }
.spotlight:has(#set-version:checked)   .p-version   { display: block; }
.spotlight:has(#set-privacy:checked)   .p-privacy   { display: block; }
.spotlight:has(#set-feedback:checked)  .p-feedback  { display: block; }

@media (max-width: 780px) {
  /* Master → detail: the list and the description become two full-width
     "screens" on one fixed-height stage; tapping a row slides to the detail,
     the Back button slides home. JS toggles .is-detail (see the page script).
     The transform rules are scoped here so the desktop layout ignores the
     class entirely. */
  /* The container clips horizontally (to hide the off-screen pane) but is NOT
     itself rounded — each pane keeps its own rounded corners, so the sliding
     description isn't sliced at the container's corners. */
  .spotlight { display: flex; flex-wrap: nowrap; gap: 0; align-items: stretch;
    grid-template-columns: none; overflow: hidden; border-radius: 0;
    height: min(78vh, 620px); }
  .spot-list, .spot-stage { flex: 0 0 100%; width: 100%; height: 100%;
    max-height: none; transition: transform .28s ease; }
  .spot-list { padding: 2px; }
  .spot-stage { padding: 22px 22px 26px; }
  .spotlight.is-detail .spot-list,
  .spotlight.is-detail .spot-stage { transform: translateX(-100%); }
  .spot-back { display: inline-flex; }
  /* No persistent selection highlight on mobile — a tap just navigates to the
     description, so the picked row needs no lit state left behind. */
  .spot-row:has(input:checked) { background: transparent; box-shadow: none; }
  .spot-row:has(input:checked) .spot-name { color: var(--ink); }
  .spot-row:has(input:checked) .spot-ic { color: var(--muted); }
  .spot-row:has(input:checked) .spot-chev { color: var(--muted); transform: none; }
}
@media (max-width: 780px) and (prefers-reduced-motion: reduce) {
  .spot-list, .spot-stage { transition: none; }
}
/* Fallback where :has() is unsupported: show the first panel so the stage is
   never blank, and drop the chevrons that imply per-row interactivity. */
@supports not selector(:has(*)) {
  .p-theme { display: block; }
  .spot-chev { display: none; }
  .p-countdowns { display: block; }
}

/* WebKit scrollbar for the compact spotlight and the widget action list */
.spot-list::-webkit-scrollbar, .spot-stage::-webkit-scrollbar, .func-list::-webkit-scrollbar { width: 6px; }
.spot-list::-webkit-scrollbar-thumb, .spot-stage::-webkit-scrollbar-thumb, .func-list::-webkit-scrollbar-thumb { background: transparent; border-radius: 3px; }
.spot-list:hover::-webkit-scrollbar-thumb, .func-list:hover::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--muted) 40%, transparent); }
.spot-stage:hover::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--log) 38%, transparent); }

/* Back control for the widget picker's mobile slide — hidden on desktop. */
.func-back { display: none; align-items: center; gap: 5px; margin: 0 0 14px -8px;
  padding: 7px 11px; border: 0; border-radius: 10px; background: none; cursor: pointer;
  color: var(--log); font: inherit; font-weight: 700; font-size: .95rem; }
.func-back > span { font-size: 1.15rem; line-height: 1; }
.func-back:hover { background: color-mix(in srgb, var(--log) 13%, transparent); }
.func-back:focus-visible { outline: 2px solid var(--log); outline-offset: 2px; }

/* ==== Carousel ==== */
.carousel { position: relative; width: min(100%, 300px); }
.carousel-viewport { overflow: hidden; border-radius: 22px; border: 1px solid var(--line);
  box-shadow: var(--shadow); background: var(--device-screen); }
.carousel-track { transition: transform .35s ease; }
.js-carousel .carousel-track { display: flex; }
.js-carousel .carousel-slide { flex: 0 0 100%; min-width: 0; }
.carousel-slide { margin: 0; }
.carousel-slide img, .carousel-slide video { width: 100%; height: auto; display: block;
  aspect-ratio: 1080 / 2400; }
.carousel-slide .shot-dark { display: none; }
:root[data-theme="dark"] .carousel-slide .shot-light { display: none; }
:root[data-theme="dark"] .carousel-slide .shot-dark { display: block; }
.carousel-nav { display: flex; align-items: center; justify-content: center; gap: 10px;
  margin-top: 14px; }
.carousel-nav[hidden] { display: none !important; }
.carousel-prev, .carousel-next { width: 30px; height: 30px; border: 1px solid var(--line);
  border-radius: 50%; background: var(--surface-raised); color: var(--muted); cursor: pointer;
  display: grid; place-items: center; padding: 0;
  transition: color .16s ease, border-color .16s ease; }
.carousel-prev:hover, .carousel-next:hover { color: var(--ink); border-color: var(--muted); }
.carousel-prev svg, .carousel-next svg { width: 14px; height: 14px; display: block; }
.carousel-dots { display: flex; gap: 6px; align-items: center; }
.carousel-dot { width: 7px; height: 7px; border-radius: 50%; border: none; padding: 0;
  background: var(--line); cursor: pointer; transition: background .2s ease; }
.carousel-dot.active { background: var(--log); }

/* Carousel inside voyage steps */
.guide-step > .carousel { grid-area: shot; justify-self: end; }

/* ==== Step card (text + media, used in widget tabs and about sections) ==== */
.step-card { display: grid; grid-template-columns: 1fr minmax(0, 300px); gap: 40px;
  align-items: start; padding: 46px 0; }
.step-card + .step-card { border-top: 1px solid var(--line); }
.step-card-body h2 { font-size: clamp(1.6rem, 2.8vw, 2.6rem); margin-bottom: 16px; }
.step-card-body h3 { margin: 24px 0 8px; font-size: 1.08rem; }
.step-card-body h3:first-child { margin-top: 0; }
.step-card-body > p, .step-card-body > ul { max-width: 650px; color: var(--muted); }
.step-card-body ul { padding-left: 22px; }
.step-card-body li { margin: 7px 0; }
.step-card-media { justify-self: end; }
.step-card-media .carousel { width: min(100%, 300px); }

/* ==== Widget tabs ==== */
.widget-tabs { max-width: none; }
.tab-bar { display: flex; gap: 8px; margin-bottom: 22px; border-bottom: 1px solid var(--line); }
.tab-label { position: relative; display: inline-flex; align-items: center; justify-content: center;
  min-width: 112px; padding: 0 2px 12px;
  color: var(--muted); font-weight: 600; font-size: .95rem; cursor: pointer;
  transition: color .16s ease; }
.tab-label:hover { color: var(--ink); }
.tab-label input { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
.tab-label:has(input:checked) { color: var(--ink); }
.tab-label:has(input:checked)::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px;
  height: 2px; background: var(--log); border-radius: 2px 2px 0 0; }
.tab-label:has(input:focus-visible) { outline: 2px solid var(--log); outline-offset: 3px; border-radius: 2px; }
.tab-panel { display: none; animation: tabFade .25s ease; }
.widget-tabs:has(#tab-countdowns:checked) .p-countdowns,
.widget-tabs:has(#tab-tasks:checked) .p-tasks,
.widget-tabs:has(#tab-notes:checked) .p-notes { display: block; }
.tab-panel .step-card { padding-top: 6px; }
@keyframes tabFade { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .tab-panel { animation: none; } }

/* ==== Clickable function rows: pick an action, its media lights up in the frame ==== */
/* Keep the action list roughly the height of the phone frame (300px wide,
   1080/2400 aspect) so a long list like Tasks scrolls inside its own box
   instead of towering over the frame. Reset to a full flow on mobile below.
   Scrollbar stays hidden until hover (matches the Settings spotlight), and the
   top/bottom edges fade only when there is hidden content that way — the
   --fade-* vars are driven by a small scroll script. */
.func-list { align-self: start; max-height: calc(300px * 20 / 9); overflow-y: auto;
  padding-right: 4px; scrollbar-width: thin; scrollbar-color: transparent transparent;
  --fade-top: 0px; --fade-bottom: 0px;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--fade-top),
    #000 calc(100% - var(--fade-bottom)), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 var(--fade-top),
    #000 calc(100% - var(--fade-bottom)), transparent 100%); }
.func-list:hover { scrollbar-color: color-mix(in srgb, var(--muted) 40%, transparent) transparent; }
.func-hint { margin: 0 0 8px; color: var(--muted); font-size: .82rem; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; }
.func-row { position: relative; display: block; padding: 15px 14px; border-radius: 14px;
  cursor: pointer; transition: background .16s ease; }
.func-row + .func-row { margin-top: 10px; }
.func-row input { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
.func-row:hover { background: var(--surface); }
.func-head { display: flex; align-items: center; gap: 8px; }
.func-head h3 { margin: 0; font-size: 1.08rem; color: var(--ink); transition: color .16s ease; }
.func-chev { margin-left: auto; color: var(--muted); font-size: 1.2rem; line-height: 1;
  transition: transform .16s ease, color .16s ease; }
.func-row > p { margin: 7px 0 0; color: var(--muted); max-width: 60ch; }
.func-row:has(input:checked) { background: var(--log-soft); }
.func-row:has(input:checked) .func-head h3,
.func-row:has(input:checked) .func-chev { color: var(--log); }
.func-row:has(input:checked) .func-chev { transform: translateX(3px); }

.func-stage { position: relative; }
/* The Create actions carry two entry-point clips in a carousel; other actions
   are a single framed clip. Both are toggled by the same :has() rule below. */
.func-carousel { display: none; width: min(100%, 300px); animation: spotFade .25s ease; }
.func-carousel .carousel { width: 100%; }
@media (prefers-reduced-motion: reduce) { .func-carousel { animation: none; } }
.func-frame { display: none; width: min(100%, 300px); overflow: hidden; border-radius: 22px;
  border: 1px solid var(--line); box-shadow: var(--shadow); background: var(--device-screen);
  animation: spotFade .25s ease; }
.func-frame img, .func-frame video { width: 100%; height: auto; display: block; aspect-ratio: 1080 / 2400; }
.func-frame .shot-dark { display: none; }
:root[data-theme="dark"] .func-frame .shot-light { display: none; }
:root[data-theme="dark"] .func-frame .shot-dark { display: block; }
@media (prefers-reduced-motion: reduce) { .func-frame { animation: none; } }

/* :has() picks the one visible frame per panel. */
.func-card:has(#fn-cd-create:checked)  .f-cd-create,
.func-card:has(#fn-cd-edit:checked)    .f-cd-edit,
.func-card:has(#fn-cd-rebook:checked)  .f-cd-rebook,
.func-card:has(#fn-cd-archive:checked) .f-cd-archive,
.func-card:has(#fn-tsk-create:checked) .f-tsk-create,
.func-card:has(#fn-tsk-edit:checked)   .f-tsk-edit,
.func-card:has(#fn-tsk-view:checked)   .f-tsk-view,
.func-card:has(#fn-tsk-skip:checked)   .f-tsk-skip,
.func-card:has(#fn-tsk-clear:checked)  .f-tsk-clear,
.func-card:has(#fn-tsk-late:checked)   .f-tsk-late,
.func-card:has(#fn-tsk-move:checked)   .f-tsk-move,
.func-card:has(#fn-tsk-drop:checked)   .f-tsk-drop,
.func-card:has(#fn-tsk-archive:checked) .f-tsk-archive,
.func-card:has(#fn-note-create:checked) .f-note-create,
.func-card:has(#fn-note-edit:checked)  .f-note-edit,
.func-card:has(#fn-note-pin:checked)   .f-note-pin,
.func-card:has(#fn-note-archive:checked) .f-note-archive { display: block; }

/* Personal Log "Your dashboard": the same .func-card, untabbed, above the card
   tabs. Only its ID-keyed frames are new; the wrapper reuses .widget-tabs for
   width, and matches the tab panels' top padding. */
.dashboard-guide .step-card { padding-top: 6px; }
.func-card:has(#fn-db-profile:checked) .f-db-profile,
.func-card:has(#fn-db-edit:checked)    .f-db-edit,
.func-card:has(#fn-db-options:checked) .f-db-options,
.func-card:has(#fn-db-all:checked)     .f-db-all { display: block; }

/* Personal Log how-to: in-text links from a journey step to the reference
   section that shows what the step only mentions. */
.jump-link { color: var(--log); text-decoration: underline; text-underline-offset: 3px; }
.jump-link:hover, .jump-link:focus-visible { text-decoration-thickness: 2px; }
#dashboard, #cards, #settings, #history, #review, #archive { scroll-margin-top: 24px; }

/* Fallback where :has() is unsupported: show each panel's first frame, drop chevrons. */
@supports not selector(:has(*)) {
  .f-cd-create, .f-tsk-create, .f-note-create { display: block; }
  .func-chev { display: none; }
}

/* ==== Responsive: carousel, step-card, tabs ==== */
@media (max-width: 900px) {
  .guide-step > .carousel { justify-self: start; margin-top: 22px; }
  .step-card { grid-template-columns: 1fr; gap: 20px; }
  .step-card-media { justify-self: start; }
  .tab-bar { gap: 6px; }
  .tab-label { min-width: 96px; padding: 0 1px 10px; font-size: .88rem; }
  /* Function picker: the action list and its media become two stacked "screens"
     that slide, exactly like the Settings spotlight — tap an action to reveal
     the phone frame, Back returns to the list. */
  /* Height is set inline per card by the picker script to match the visible
     slide (list vs media); the transition animates it in step with the slide so
     the hidden, often-taller slide never leaves dead space below the shown one. */
  .func-card { display: flex; flex-wrap: nowrap; gap: 0; overflow: hidden; align-items: stretch;
    transition: height .28s ease; }
  .func-card .func-list, .func-card .func-stage {
    flex: 0 0 100%; width: 100%; overflow: visible;
    transition: transform .28s ease; }
  /* Keep the action list a scroll box on mobile too, capped to roughly the
     phone frame's height, so a long list (Tasks) fits the screen alongside the
     media instead of towering over it. */
  .func-card .func-list { order: 1; padding-right: 0;
    align-self: start; max-height: 62vh; overflow-y: auto; }
  .func-card .func-stage { order: 2; justify-self: stretch; }
  .func-card.is-detail .func-list,
  .func-card.is-detail .func-stage { transform: translateX(-100%); }
  .func-frame, .func-carousel { width: min(72%, 260px); margin: 6px auto 0; }
  .func-back { display: inline-flex; }
  .func-hint { display: none; }
  /* No highlight on mobile — a tap just navigates, so drop the selection and
     hover states too (the desktop selection highlight would otherwise linger
     after Back). */
  .func-row:has(input:checked) { background: transparent; }
  .func-row:has(input:checked) .func-head h3 { color: var(--ink); }
  .func-row:has(input:checked) .func-chev { color: var(--muted); transform: none; }
  .func-row:hover { background: transparent; }
}

/* ==== Gacha Eats how-to: an app screenshot beside each of the first steps ====== */
/* Same idea as the Personal Log voyage steps, minus the boat rail — and with the
   step number sitting INSIDE the copy (a .step-n, like the Personal Log steps)
   rather than in its own column, so on mobile the copy leads cleanly instead of a
   lone number stacking above it. Two columns: body · phone. */
.guide-list.eats-guide { max-width: 1140px; }
.eats-guide .guide-step {
  grid-template-columns: minmax(0, 1fr) minmax(0, 300px);
  grid-template-areas: "body shot";
  gap: 40px; align-items: start;
}
.eats-guide .step-body { grid-area: body; }
.eats-guide .step-n { color: var(--orange); }
.eats-guide .guide-step > .carousel { grid-area: shot; justify-self: end; }

@media (max-width: 900px) {
  .eats-guide .guide-step {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "body" "shot";
    gap: 20px;
  }
  .eats-guide .guide-step > .carousel { justify-self: start; margin-top: 22px; }
}

/* ==== Gacha Eats how-to: Banners + Log widget tabs and their action frames ====== */
/* Reuses the shared .widget-tabs / .func-card structure; only the ID-keyed
   visibility toggles are page-specific, plus an orange accent (the shared rules
   default to the Personal Log cobalt --log). */
.widget-tabs:has(#tab-banners:checked) .p-banners,
.widget-tabs:has(#tab-log:checked) .p-log { display: block; }

.func-card:has(#fn-ban-build:checked)    .f-ban-build,
.func-card:has(#fn-ban-pool:checked)     .f-ban-pool,
.func-card:has(#fn-ban-arrange:checked)  .f-ban-arrange,
.func-card:has(#fn-ban-edit:checked)     .f-ban-edit,
.func-card:has(#fn-ban-global:checked)   .f-ban-global,
.func-card:has(#fn-log-add:checked)      .f-log-add,
.func-card:has(#fn-log-required:checked) .f-log-required,
.func-card:has(#fn-log-dish:checked)     .f-log-dish,
.func-card:has(#fn-log-mark:checked)     .f-log-mark,
.func-card:has(#fn-log-manage:checked)   .f-log-manage { display: block; }

/* :has()-unsupported fallback: show each tab's first action frame. */
@supports not selector(:has(*)) {
  .f-ban-arrange, .f-log-add { display: block; }
}

/* Orange accent for the tabs + action rows on the eats page. */
.theme-eats .tab-label:has(input:checked)::after { background: var(--orange); }
.theme-eats .tab-label:has(input:focus-visible) { outline-color: var(--orange); }
.theme-eats .func-row:has(input:checked) { background: var(--orange-soft); }
.theme-eats .func-row:has(input:checked) .func-head h3,
.theme-eats .func-row:has(input:checked) .func-chev { color: var(--orange-dark); }

/* ==== How-to page titles ===================================================== */
/* Both guides opened on an <h2>, so neither page had an <h1>. They are <h1> now,
   inside .guide-intro, and these two rules keep them looking exactly as the <h2>
   did: h2 size, the browser's default h2 bottom margin (.83em), and no accent
   serif on the <span>, which .page-intro h1 span would otherwise apply. */
.page-intro.guide-intro h1 { font-size: clamp(2.1rem, 4vw, 4rem); margin: 0 0 .83em; }
.page-intro.guide-intro h1 span { font-family: inherit; font-weight: inherit; color: inherit; }

/* ==== Keyboard focus on the function and settings lists ====================== */
/* The radios in .func-row and .spot-row are visually hidden, so a keyboard user
   tabbing into either list had no focus ring. Inset, because both lists scroll
   and would clip a ring drawn outside the row. */
.func-row:has(input:focus-visible),
.spot-row:has(input:focus-visible) { outline: 2px solid var(--log); outline-offset: -2px; }
.theme-eats .func-row:has(input:focus-visible) { outline-color: var(--orange); }

/* ==== Post tables on narrow screens ========================================== */
/* A wide table scrolls inside itself instead of widening the page: the four-column
   table in the mechanics post pushed a 375px phone to 411px. Narrow screens only,
   because display: block stops a table stretching to full width on desktop. */
@media (max-width: 600px) {
  .post table { display: block; overflow-x: auto; }
}

/* ==== Overview heroes on phones ============================================== */
/* The hero is two columns, copy beside the phone clip, and nothing collapsed it,
   so on a phone the clip shrank to a ~60px sliver next to the text. Below 600px
   it becomes one column, and display: contents on the copy wrapper lets the clip
   sit between the title and the description: pill, title, clip, text, button.
   Tablets keep two columns, which still fit at 768px. */
@media (max-width: 600px) {
  .product-hero { display: flex; flex-direction: column; align-items: flex-start; gap: 0; min-height: 0; }
  .product-hero-copy { display: contents; }
  .product-hero-copy > .pill { order: 1; }
  .product-hero h1 { order: 2; }
  .product-hero .hero-shot { order: 3; align-self: center; width: min(64%, 240px); margin: 6px 0 44px; }
  .product-hero-copy > p { order: 4; }
  .product-hero .hero-actions { order: 5; }
}

/* ==== Sideways-scrolling row (.hs) ============================================ */
/* A row that scrolls sideways when it doesn't fit: no scrollbar, a fade on each
   edge that has more content, and round arrows on hover-capable screens. On touch
   screens the arrows are display: none, not just transparent, because an invisible
   button still catches taps; the swipe is the control there and the fade the hint.
   hscroll.js keeps data-clip-left / data-clip-right on .hs in step with the scroll
   position, and everything below keys off those, so a row that fits gets no mask
   and no arrows. The arrows sit outside the viewport so its mask never fades them.
   Markup: .hs > .hs-viewport > .hs-content, then the two .hs-arrow buttons. */
.hs { --hs-fade: 20px; --hs-arrow: 24px; position: relative; min-width: 0; }
.hs-viewport { overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none; }
.hs-viewport::-webkit-scrollbar { display: none; }
.hs-content { width: max-content; }
.hs[data-clip-left] .hs-viewport, .hs[data-clip-right] .hs-viewport {
  --hs-l: #000; --hs-r: #000;
  -webkit-mask-image: linear-gradient(to right, var(--hs-l), #000 var(--hs-fade), #000 calc(100% - var(--hs-fade)), var(--hs-r));
  mask-image: linear-gradient(to right, var(--hs-l), #000 var(--hs-fade), #000 calc(100% - var(--hs-fade)), var(--hs-r)); }
.hs[data-clip-left] .hs-viewport { --hs-l: transparent; }
.hs[data-clip-right] .hs-viewport { --hs-r: transparent; }
.hs-arrow { position: absolute; top: 50%; translate: 0 -50%; display: none; place-items: center; width: var(--hs-arrow); height: var(--hs-arrow); padding: 0;
  border: 1px solid var(--line); border-radius: 50%; background: var(--surface-raised); color: var(--ink); box-shadow: 0 1px 3px var(--shadow-color);
  cursor: pointer; opacity: 0; transition: opacity .15s ease; }
.hs-arrow svg { width: 14px; height: 14px; }
.hs-arrow--left { left: 0; }.hs-arrow--right { right: 0; }
@media (hover: hover) {
  .hs[data-clip-left] .hs-arrow--left, .hs[data-clip-right] .hs-arrow--right { display: grid; }
  .hs:hover .hs-arrow { opacity: 1; }
}

/* ==== Privacy contents bar on phones and tablets ============================= */
/* Above 900px the contents list sits in the sticky left column. Below it the
   column stacks above the policy and the list used to scroll away with the title,
   so it was no help from the second screen on. Now it's a one-line bar pinned to
   the top of the screen that scrolls sideways (.hs), with the section being read
   underlined. It borrows the how-to pages' tab look (.tab-bar: rail plus accent
   underline) rather than the subnav's rounded pills, because the subnav sits right
   above it and two pill rows in a row would read as one control.

   Sticky only holds while the element's parent is on screen, and the parent is
   the <aside>, which ends with the title. display: contents drops the aside's box
   so the bar's parent becomes .policy-layout, which runs the length of the policy.
   The layout goes from grid to block for that, or the aside's children would each
   become a grid row with the 45px gap between them.

   --policy-bar is the bar's height (10 + 24 + 10 link, 1 border). Sections use it
   as scroll-margin-top so a jump doesn't park the heading under the bar, and
   policy-index.js reads that margin back to know where "the top" is. */
@media (max-width: 900px) {
  .policy-layout { --policy-bar: 45px; display: block; }
  .policy-layout aside { display: contents; }
  .policy-content section { scroll-margin-top: var(--policy-bar); }

  /* Full bleed: the negative margin cancels .policy-layout's 24px side padding, and
     the content's 16px padding plus each link's 8px puts the first label back on
     the page's 24px gutter. The fade is wider than that gutter so labels thin out
     as they reach it rather than at the screen edge. */
  .policy-index { --hs-fade: 32px; position: sticky; top: 0; z-index: 5; margin: 40px -24px 8px; background: var(--paper); border-bottom: 1px solid var(--line); }
  .policy-index .hs-viewport { overflow-x: auto; overflow-y: hidden; }
  .policy-index .hs-content { display: flex; gap: 4px; padding-inline: 16px; }
  .policy-index a { position: relative; padding: 10px 8px; line-height: 24px; border-left: 0; white-space: nowrap; font-size: .88rem; font-weight: 600; }
  .policy-index a.is-current::after { content: ""; position: absolute; left: 8px; right: 8px; bottom: 0; height: 2px; border-radius: 2px 2px 0 0; background: var(--index-accent); }
  /* The viewport clips on both axes, so the site's outset focus ring goes inside. */
  .policy-index a:focus-visible { outline-offset: -3px; border-radius: 8px; }
}

/* ==== Closed-test pages (personal-log-test, gacha-eats-test) ================= */
/* Three numbered cards, one link each, in the order a tester has to do them. The
   buttons sit at the bottom of each card (margin-top: auto) so the three line up
   on desktop whatever the copy above them runs to. Temporary pages, see
   site-conventions.md §12. */
.join-section { max-width: 1240px; margin: 0 auto; padding: 0 24px; }
.join-steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; margin: 0; padding: 0; list-style: none; }
.join-steps li { display: flex; flex-direction: column; align-items: flex-start; padding: 32px; border-radius: 16px; background: var(--surface); border: 1px solid var(--line); }
.join-steps h2 { margin: 0 0 12px; font-size: clamp(1.5rem, 2.2vw, 1.9rem); letter-spacing: -.03em; }
.join-steps p { color: var(--muted); }
.join-steps .button { flex: none; margin-top: auto; }
.join-note { max-width: 66ch; margin: 26px auto 0; color: var(--muted); text-align: center; }
.try-section { max-width: 900px; margin: 0 auto; padding: 0 24px; }
.try-section h2 { margin: 14px 0 22px; }
.try-section > p:not(.eyebrow), .try-section ul { max-width: 66ch; color: var(--muted); }
.try-section ul { margin: 0 0 16px; padding-left: 22px; }
.try-section li { margin: 9px 0; }
.try-section a { color: var(--green); text-decoration: underline; text-underline-offset: 3px; }
/* .step-n and the links default to the Personal Log palette; the eats page needs
   the same swap .eats-guide and .faq-outro already make. */
.theme-eats .join-steps .step-n { color: var(--orange); }
.theme-eats .try-section a { color: var(--eats-text); }
@media (max-width: 900px) {
  .join-steps { grid-template-columns: 1fr; }
  .join-steps li { padding: 26px; }
}
