/* greypaint.app
   Laid out the way Shiori is: one narrow column, centred on the page, with
   everything left-aligned inside it. Headings are a bold line over a grey
   line — no rules, no mono kickers. Features are a two-column grid of small
   icon, label, one line. Whitespace does the dividing.
   Palette and type are the app's own: warm paper, one wasabi accent. */

/* ---------------- palette ----------------
   One set of tokens, each carrying both values through light-dark(), so the
   light and dark palettes can never drift apart. Which half is used is
   decided entirely by color-scheme on the root: `light dark` follows the
   system, and the switcher pins it to one by setting data-theme. The values
   are the app's own tokens (GP.ground, surface, ink, n600, accent), so the
   site and the window it shows agree.

   One token behaves differently from the rest: --shot-bg. In light it is the
   exact grey inside the screenshots, letting a shot bleed into the panel with
   no seam. A light screenshot cannot bleed into a dark panel, so in dark it
   becomes the panel colour and --shot-frame draws the hairline that frames it
   instead — a deliberate object on the surface rather than a failed seam. */

:root {
  color-scheme: light dark;   /* auto: whichever the system asks for */

  --paper:   light-dark(#F4F3F1, #141413);
  --panel:   light-dark(#FAF9F7, #1C1C1A);
  --well:    light-dark(#EAE9E6, #262623);
  /* GP.ground — the colour the app paints behind Settings. The screenshots
     were captured on a wide-gamut display and carried its profile, which
     read 3 values dark on the web; they're converted to sRGB now, so this
     token, the app and the shots are all the same grey with no seam. */
  --shot-bg: light-dark(#EFEEEC, #1C1C1A);
  --ink:     light-dark(#1B1A19, #F2F1EE);
  --muted:   light-dark(#77736E, #A5A29D);
  --faint:   light-dark(#B4B1AC, #6E6B67);
  --wasabi:  light-dark(#CDD152, #C4C84C);
  --on-wasabi: #1A1B0A;   /* the app's onAccent — the only ink legible on wasabi */
  /* olive carries the accent where text must read on the page ground; the
     light value is far too dark to survive on the dark ground, so there it
     becomes the accent itself, exactly as GP.accent700 does in the app. */
  --olive:   light-dark(#5F6318, #C4C84C);
  --edge:    light-dark(rgba(27, 26, 25, 0.08), rgba(242, 241, 238, 0.10));
  /* the same rule, weighted for the darker screenshot ground so it reads
     at one strength the whole way across a split card */
  --edge-shot: light-dark(rgba(27, 26, 25, 0.09), rgba(242, 241, 238, 0.10));
  --wash:      light-dark(rgba(27, 26, 25, 0.035), rgba(242, 241, 238, 0.04));
  /* absent in light, where the shot bleeds into the panel; the hairline that
     makes it an object in dark */
  --shot-frame: light-dark(transparent, rgba(242, 241, 238, 0.10));

  --sans: "Archivo", -apple-system, "Helvetica Neue", Arial, sans-serif;
  --mono: "DM Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --col:  700px;
  --mid:  880px;   /* compact feature container, wider than the reading column */
  --wide: 1080px;
}

/* The switcher pins the scheme; everything above follows from these two. */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark; }

/* The one thing that isn't a colour, so light-dark() can't carry it: the
   footer mark is a flat near-black glyph baked into a PNG and vanishes
   against the dark ground. It is one colour on transparency, which makes
   inversion exact rather than approximate. Two selectors say the same thing
   because the auto case has to be read from the media query — but they set
   two variables, not a palette, so there is nothing here to drift. */
:root { --mk-filter: none; --mk-opacity: .85; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --mk-filter: invert(1); --mk-opacity: .5; }
}
:root[data-theme="dark"] { --mk-filter: invert(1); --mk-opacity: .5; }

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: inherit; }
:focus-visible { outline: 2px solid var(--olive); outline-offset: 3px; border-radius: 4px; }

.col  { width: min(var(--col), 100% - 44px);  margin-inline: auto; }
.wide { width: min(var(--wide), 100% - 44px); margin-inline: auto; }
.mid  { width: min(var(--mid),  100% - 44px); margin-inline: auto; }

h1, h2, h3 { margin: 0; font-weight: 500; text-wrap: balance; }
p { margin: 0; }

/* ---------------- buttons ---------------- */

.btn {
  position: relative; isolation: isolate; overflow: hidden;
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--sans); font-size: 13.5px; font-weight: 500;
  text-decoration: none; cursor: pointer;
  padding: 9px 16px;
  border: 1px solid var(--ink); border-radius: 9px;
  background: var(--ink); color: var(--paper);
  transition: color 180ms ease, transform 220ms cubic-bezier(.22,.61,.36,1);
}

/* Wasabi blooms from wherever the pointer crossed the edge and drains back
   out the way it left — the script feeds --mx/--my; without it the circle
   opens from the centre, which is also what a keyboard focus gets. The ink
   border stays put through all of it, so the filled button reads as an
   outlined chip rather than a solid slab. */
.btn::before {
  content: ""; position: absolute; z-index: -1;
  top: var(--my, 50%); left: var(--mx, 50%);
  width: 480px; height: 480px; margin: -240px 0 0 -240px;
  border-radius: 50%;
  background: var(--wasabi);
  transform: scale(0);
  transition: transform 520ms cubic-bezier(.19,.72,.3,1);
}
.btn:hover, .btn:focus-visible { color: var(--on-wasabi); transform: translateY(-1px); }
.btn:hover::before, .btn:focus-visible::before { transform: scale(1); }
.btn:active { transform: translateY(0) scale(.985); }
.btn svg { flex: none; }

/* The secondary button keeps out of the accent's way — same bloom, quiet fill. */
.btn-ghost { background: var(--panel); color: var(--ink); border-color: var(--edge); }
.btn-ghost::before { background: var(--well); }
.btn-ghost:hover, .btn-ghost:focus-visible { color: var(--ink); }

/* ---------------- hero ---------------- */

.hero { position: relative; padding: 230px 0 0; text-align: center; --mark: 600px; }
.hero .mark {
  position: absolute; z-index: 0; top: -220px; left: 50%;
  width: var(--mark); height: var(--mark); display: block; margin: 0;
  opacity: .24; transform: translateX(-50%); pointer-events: none;
  transition: opacity 240ms ease;
}
.hero > :not(.mark):not(.markbtn) { position: relative; z-index: 1; }

/* The hero mark is the same switch as the one in the footer. The image is
   600px square and hangs off the top of the page, but the glyph inside it is
   half that wide and stops well above the headline — so the hit area is the
   glyph, not the box it arrived in. A button over the whole image would flip
   the theme on a click into the empty air beside the headline, which would
   read as a bug rather than a control. Both are sized from --mark, so they
   can't come apart at the breakpoint. */
.hero .markbtn {
  position: absolute; z-index: 2; top: 0; left: 50%;
  width: calc(var(--mark) * .5); height: calc(var(--mark) * .29);
  transform: translateX(-50%);
}
/* The glyph sits off the mark's lower-right corner, on the diagonal, where the
   hero has nothing else in it. */
.hero .markbtn .fold {
  position: absolute; left: 100%; top: 100%;
  margin: -22px 0 0 -20px;
}
.hero .markbtn:hover + .mark { opacity: .34; }
.hero .markbtn:active + .mark { opacity: .28; }
.hero .markbtn:focus-visible {
  outline: 2px solid var(--olive); outline-offset: 10px; border-radius: 14px;
}
/* Shiori sets the subline at the SAME size as the headline and separates the
   two by colour alone. That one move is most of the look. */
.hero h1 {
  font-size: clamp(30px, 3.7vw, 42px);
  line-height: 1.14; letter-spacing: -.03em;
  font-weight: 500; max-width: 20ch; margin-inline: auto;
}
.hero .sub { margin: 16px auto 0; font-size: 17px; color: var(--muted); max-width: 42ch; }
.hero .actions { display: flex; justify-content: center; gap: 8px; flex-wrap: wrap; margin-top: 26px; }
.hero .req { font-family: var(--mono); font-size: 11.5px; color: var(--muted); margin: 14px auto 0; }

.scribbled { position: relative; display: inline-block; }
.scribbled svg {
  position: absolute; left: -.05em; right: -.05em; bottom: -.16em;
  width: calc(100% + .1em); height: .3em; overflow: visible;
}
.head .scribbled svg { height: .26em; bottom: -.13em; }
.scribbled path {
  fill: none; stroke: var(--wasabi);
  stroke-width: 5; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 620; stroke-dashoffset: 620;
  animation: draw 1.9s cubic-bezier(.62, .02, .35, 1) .6s forwards;
}
@keyframes draw { to { stroke-dashoffset: 0; } }

/* ---------------- the product shot ---------------- */

.shot { margin-top: 52px; }
.shot img { display: block; width: 100%; height: auto; border-radius: 6px; }

/* ---------------- sections ---------------- */

section { padding: 108px 0 0; }

/* Ours, not theirs: one statement carrying the scribble, and a quiet line
   under it at reading size. */
.head h2 {
  font-size: clamp(22px, 2.5vw, 29px);
  line-height: 1.22; letter-spacing: -.022em;
  font-weight: 500; max-width: 22ch;
}
.head p { margin-top: 12px; font-size: 15px; color: var(--muted); max-width: 44ch; }

.grid {
  margin-top: 46px;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 44px 40px;
}
/* Instead of a stock outline glyph, each cell shows the actual thing the
   source hands over — a version moving, a token, a tag. Only this product
   has that to show. */
.item .ic { display: block; color: var(--ink); margin-bottom: 16px; }
.item .ic img { width: 26px; height: 26px; display: block; }

.chip {
  display: inline-flex; align-items: baseline; gap: 7px;
  margin-bottom: 16px; padding: 6px 10px;
  background: var(--panel); border: 1px solid var(--edge); border-radius: 7px;
  font-family: var(--mono); font-size: 11.5px; font-variant-numeric: tabular-nums;
  color: var(--muted); white-space: nowrap;
}
.chip b { font-weight: 500; color: var(--olive); }
.chip .ar { color: var(--faint); }
.item h3 { font-size: 15px; font-weight: 500; letter-spacing: -.004em; }
.item p { margin-top: 3px; font-size: 15px; color: var(--muted); max-width: 30ch; }
.item .act { display: flex; gap: 6px; margin-top: 14px; }
.item .act .btn { padding: 7px 13px; font-size: 12.5px; }
.item .act .chev { padding: 7px 10px; }

/* a row of app icons standing in for the icon, as Shiori does for browsers */
.item .row { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
.item .row img { width: 36px; height: 36px; display: block; }
/* Only the sources grid ends on a full-width cell — that last one carries the
   row of app icons. Left unscoped this rule also broke the facts row below
   into a ragged 2 / 1 / 1. */
.grid.sources .item:last-child { grid-column: 1 / -1; }
.grid.sources .item:last-child .row { gap: 6px; }

/* Four claims, all of them a number that is zero (or one). They read as a set,
   so they sit in one horizontal band rather than a block of paragraphs — the
   chips line up and the eye runs straight across them. */
.grid.facts {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 28px 24px;
}
.grid.facts .chip { margin-bottom: 12px; }
.grid.facts p { max-width: none; font-size: 14px; }

@media (max-width: 860px) { .grid.facts { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) {
  .grid { grid-template-columns: 1fr; gap: 32px; }
  .grid.facts { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 26px 20px; }
}

/* One compact container for both controls; the images stay on the panel surface. */
.feature-box {
  margin-top: 40px;
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 14px;
  overflow: hidden;
}
.split {
  margin-top: 0;
  display: grid; grid-template-columns: minmax(250px, .43fr) minmax(0, 1.57fr);
  background: transparent;
}
/* The rule is drawn per column, not across the row: a border on .split
   paints over the card's panel white, which leaves it a 4-value step on
   the grey side and 18 on the white — one line, two weights. Each column
   blending against its own ground fixes that. */
.split + .split .l { border-top: 1px solid var(--edge); }
.split + .split .r { border-top: 1px solid var(--edge-shot); }
.split .l { padding: 28px 26px; display: flex; flex-direction: column; justify-content: center; gap: 8px; }
.split .l .ic { color: var(--ink); }
.split .l h3 { font-size: 15px; font-weight: 500; margin-top: 6px; }
.split .l p { font-size: 15px; color: var(--muted); }
.split .r {
  padding: 20px 22px; background: var(--shot-bg);
  /* on the screenshot ground in light mode, like the horizontal rule above */
  border-left: 1px solid var(--edge-shot);
  min-width: 0;
  display: flex; align-items: center; justify-content: center;
}
.split .r img {
  display: block; width: 100%; max-width: 100%; height: auto; border-radius: 7px;
  /* a hairline, not a border: box-shadow takes no space, so the shot sits at
     the same size in both themes and only gains an edge in dark */
  box-shadow: 0 0 0 1px var(--shot-frame);
}
@media (max-width: 760px) {
  .split { grid-template-columns: 1fr; }
  .split .r { border-left: 0; border-top: 1px solid var(--edge-shot); }
}

/* ---------------- the fleet ---------------- */

.fleet-block { margin-top: 34px; display: flex; align-items: center; gap: 12px; }
.fleet {
  display: flex; align-items: center; padding-left: 11px;
  -webkit-mask-image: linear-gradient(90deg, #000 72%, rgba(0,0,0,.3) 90%, transparent 100%);
          mask-image: linear-gradient(90deg, #000 72%, rgba(0,0,0,.3) 90%, transparent 100%);
}
.fleet img {
  width: 42px; height: 42px; display: block; margin-left: -11px;
  transition: margin-left .45s cubic-bezier(.22,1,.36,1), transform .45s cubic-bezier(.22,1,.36,1);
}
.fleet:hover img { margin-left: -2px; }
.fleet img:hover { transform: translateY(-5px); }
.fleet-more { font-family: var(--mono); font-size: 11.5px; color: var(--muted); white-space: nowrap; }
@media (max-width: 620px) { .fleet img:nth-child(n+9) { display: none; } }

/* ---------------- close + footer ---------------- */

.close { padding: 116px 0 0; }
.close h2 { font-size: clamp(24px, 2.8vw, 32px); letter-spacing: -.024em; max-width: 18ch; }
.close .actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 22px; }
.close .req { font-family: var(--mono); font-size: 11.5px; color: var(--muted); margin-top: 14px; }

/* ---------------- FAQ ---------------- */

.faq { margin-top: 44px; }
.faq details { border-top: 1px solid var(--edge); }
.faq details:last-of-type { border-bottom: 1px solid var(--edge); }
.faq summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  padding: 18px 2px;
  font-size: 15px; font-weight: 500; letter-spacing: -.004em;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: ""; flex: none;
  width: 8px; height: 8px;
  border-right: 1.4px solid var(--muted); border-bottom: 1.4px solid var(--muted);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform 180ms ease;
}
.faq details[open] summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
.faq .a { padding: 0 2px 20px; font-size: 15px; color: var(--muted); max-width: 60ch; }

/* ---------------- footer ---------------- */

footer {
  margin-top: 120px; padding: 0 0 72px;
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  text-align: center;
}
footer .mk {
  width: 32px; height: 32px; display: block; margin-bottom: 10px;
  filter: var(--mk-filter); opacity: var(--mk-opacity);
  transition: opacity 200ms ease, transform 260ms cubic-bezier(.22, 1, .36, 1);
}

/* The mark is the theme switch. It is the one thing on the page that is
   already the product's own face, so making it the control adds no chrome —
   the page still has none. The script wraps the image in this button, so with
   scripting off the mark stays an image rather than a button that does nothing.

   Under it, a caret pointing back up at the mark and the state in one word:
   quiet enough to ignore, present enough to be found, and the word is what
   makes the click legible rather than a guess. */
footer .mkbtn {
  display: flex; flex-direction: column; align-items: center; gap: 9px;
  margin-bottom: 10px;
}
footer .mkbtn .mk { margin-bottom: 0; }
footer .mkbtn:hover .mk { opacity: 1; transform: translateY(-1px); }
footer .mkbtn:active .mk { transform: translateY(0) scale(.94); }
footer .mkbtn:focus-visible {
  outline: 2px solid var(--olive); outline-offset: 7px; border-radius: 10px;
}

/* ---------------- the fold ----------------
   One disc with a bite out of it and eight rays. In light the bite is parked
   outside the glyph and the rays are out; in dark the bite slides in, the disc
   grows into it and the rays fold away. Same object throughout — nothing is
   swapped, so the change is a movement rather than a cut. */

.themebtn {
  appearance: none; background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--faint);
  transition: color 200ms ease;
}
.themebtn:hover { color: var(--muted); }
.themebtn .fold { display: block; }
.themebtn .tg { display: block; overflow: visible; }
.themebtn .tg .bite,
.themebtn .tg .disc,
.themebtn .tg .rays {
  transform-box: view-box; transform-origin: 7.5px 7.5px;
  transition: transform 520ms cubic-bezier(.22, 1, .36, 1), opacity 260ms ease;
}
.themebtn[data-on="dark"] .tg .bite { transform: translate(-3.6px, 3.6px); }
.themebtn[data-on="dark"] .tg .disc { transform: scale(1.42); }
.themebtn[data-on="dark"] .tg .rays { opacity: 0; transform: scale(.55); }
.themebtn:active .tg .disc { transform: scale(.86); }
.themebtn[data-on="dark"]:active .tg .disc { transform: scale(1.24); }
footer .links { display: flex; gap: 18px; flex-wrap: wrap; justify-content: center; }
footer a { font-size: 13.5px; color: var(--muted); text-decoration: none; }
footer a:hover { color: var(--ink); }
/* version, OS requirement and price — information, not decoration, so it
   takes the readable colour rather than the faint one. */
footer .fine { font-family: var(--mono); font-size: 11px; color: var(--muted); margin-top: 6px; }

/* ---------------- legal / support pages ---------------- */

.legal { padding: 72px 0 120px; }
.legal-back { font-family: var(--mono); font-size: 12px; color: var(--muted); text-decoration: none; }
.legal-back:hover { color: var(--ink); }
.legal-head { padding: 116px 0 52px; border-bottom: 1px solid var(--edge); }
.legal-head .kicker { font-family: var(--mono); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--olive); }
.legal-head h1 { margin-top: 14px; font-size: clamp(38px, 7vw, 72px); line-height: 1.02; letter-spacing: -.045em; }
.legal-head .lead { max-width: 34ch; margin-top: 22px; font-size: 19px; color: var(--muted); }
.legal-copy { max-width: 620px; padding-top: 46px; }
.legal-copy p { margin-top: 24px; font-size: 16px; }
.legal-copy strong { font-weight: 500; }
.legal-copy a:not(.btn) { color: var(--olive); }
.legal-copy .btn { margin-top: 8px; }
.legal-copy .fine { margin-top: 58px; }

/* ---------------- support form ---------------- */

.support-form {
  margin-top: 48px; padding: 28px;
  background: var(--panel); border: 1px solid var(--edge); border-radius: 14px;
}
.support-form-head { display: flex; align-items: baseline; justify-content: space-between; gap: 20px; padding-bottom: 24px; border-bottom: 1px solid var(--edge); }
.form-kicker { font-family: var(--mono); font-size: 11px !important; letter-spacing: .12em; text-transform: uppercase; color: var(--olive); }
.support-form h2 { margin-top: 8px; font-size: 24px; letter-spacing: -.025em; }
.form-note, .file-note, .form-status { color: var(--muted); font-size: 12px !important; }
.support-form label:not(.trap):not(.file-pick) { display: block; margin-top: 22px; }
.support-form label > span { display: block; margin-bottom: 7px; font-size: 13px; font-weight: 500; }
.support-form input[type="email"], .support-form select, .support-form textarea {
  width: 100%; padding: 11px 12px; border: 1px solid var(--edge); border-radius: 8px;
  background: var(--paper); color: var(--ink); font: inherit; font-size: 14px; outline: none;
}
.support-form textarea { resize: vertical; min-height: 124px; }
.support-form input:focus, .support-form select:focus, .support-form textarea:focus { border-color: var(--olive); box-shadow: 0 0 0 3px color-mix(in srgb, var(--olive) 16%, transparent); }
.support-form-foot { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-top: 22px; }
.file-pick { color: var(--olive); cursor: pointer; font-size: 13px; }
.file-pick input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.file-pick:hover { color: var(--ink); }
.file-note { margin-top: 9px; }
.file-note.bad, .form-status.bad { color: #9a3b2e; }
.form-status.good { color: var(--olive); }
.form-status { min-height: 1.55em; margin-top: 16px; }
.support-form button:disabled { opacity: .55; cursor: wait; transform: none; }
.trap { position: absolute !important; left: -10000px !important; width: 1px !important; height: 1px !important; overflow: hidden !important; }

@media (max-width: 620px) {
  .legal { padding-top: 36px; }
  .legal-head { padding-top: 82px; }
  .support-form { padding: 20px; }
  .support-form-head, .support-form-foot { align-items: flex-start; flex-direction: column; }
}

/* ---------------- entrances ---------------- */

.js [data-rise] { opacity: 0; transform: translateY(10px); }
.js [data-rise].shown {
  opacity: 1; transform: none;
  transition: opacity .5s ease, transform .55s cubic-bezier(.22, 1, .36, 1);
}

@media (max-width: 880px) {
  .hero { padding-top: 126px; }
  .hero { --mark: 440px; }
  .hero .mark { top: -168px; }
  section { padding-top: 76px; }
  .close { padding-top: 84px; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .head .scribbled svg { height: .26em; bottom: -.13em; }
.scribbled path { animation: none; stroke-dashoffset: 0; }
  .js [data-rise] { opacity: 1 !important; transform: none !important; }
  * { transition-duration: 1ms !important; }
}
