/* Selfish POS — brand tokens. THE SINGLE SOURCE.
   (c) Demetri Gregorakis / SELFISH POS

   Lifted verbatim from brand/brand-guidelines.md v1.0 (Sept 2026).
   If this file and brand/guidelines-source.html disagree, THE HTML WINS.

   Every surface imports this. Nothing downstream declares a color, a face or
   a measure. A hex code found elsewhere in this system is a bug, not a
   variation — the only exceptions are a <meta name="theme-color"> and an
   inline SVG favicon, neither of which can read a variable. */


:root{
  /* ── Color. SIX TOKENS. Nothing else exists. ─────────────────────────── */
  --ink:        #252821;  /* text, borders, dark bands                       */
  --sage:       #747F64;  /* THE signature. Primary buttons, active, arrow 2 */
  --sage-light: #A6AF97;  /* arrow 3 and tonal steps ON INK ONLY             */
  --paper:      #F0F3ED;  /* alternating band background                     */
  --white:      #FFFFFF;  /* default ground                                  */
  --alert:      #B03A28;  /* SEMANTIC ONLY — errors, danger, 86'd            */
  --link:       #5D6A50;  /* link ink; sage carries the underline            */

  /* SAGE-LIGHT IS RESTRICTED. It never touches a white or paper ground — it
     is too close to the background and the element disappears. Two jobs only:
     the third arrow, and tonal steps on ink. Never type, never a rule, never
     an icon, never a button, never a fill on white or paper.
     No gradients. No tints outside this set. No second accent. Red is never
     decorative. Body copy is NEVER sage on white. */

  --rule:     2px solid var(--ink);   /* the single container device */
  --hairline: 1px solid #E0E4DB;

  /* ── Type. Three faces, fixed jobs. ───────────────────────────────────── */
  /* League Spartan 800 — logotype and EVERY heading. Always uppercase,
     letter-spacing .07em. NEVER body copy.
     Instrument Sans — everything read as prose.
     IBM Plex Mono — money, counts, timestamps, ticket numbers, labels. */
  --font-display: 'League Spartan', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-body:    'Instrument Sans', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono:    'Instrument Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

  --track-logo: .07em;   /* League Spartan logotype + headings */
  --track-head: .05em;   /* display and heading scale          */
  --track-label:.14em;   /* mono uppercase labels              */
  --track-wide: .18em;

  /* ── Space ────────────────────────────────────────────────────────────── */
  --s1: 8px; --s2:14px; --s3:20px; --s4:28px;
  --s5:40px; --s6:56px; --s7:72px; --s8:96px;

  --page:    1060px;
  --measure: 68ch;   /* max line length. Hard rule. */
  --tap:     48px;
}

/* ── The type scale ─────────────────────────────────────────────────────── */
.t-display{font-family:var(--font-display);font-weight:800;text-transform:uppercase;
  font-size:clamp(40px,7vw,56px);line-height:1;letter-spacing:var(--track-head)}
.t-heading{font-family:var(--font-display);font-weight:800;text-transform:uppercase;
  font-size:26px;line-height:32px;letter-spacing:var(--track-head)}
.t-subhead{font-size:17px;line-height:26px;font-weight:600}
.t-body{font-size:15px;line-height:24px}
.t-label{font-family:var(--font-mono);font-size:11px;line-height:18px;
  text-transform:uppercase;letter-spacing:var(--track-label)}

/* ── The mark. No image files. ──────────────────────────────────────────── */
/* Three chevrons stepping forward, each lighter than the last. Remove one and
   the rhythm breaks. The lighter leading arrows imply motion WITHOUT a
   gradient, blur or shadow — that is the whole trick, and why it will not date.
   Scale by changing width/height ONLY; clip-path and gap ratio never change.
   Minimum width 90px digital. Below that, the app icon. */
.mark{display:flex; gap:3px}                    /* gap = arrow width ÷ 4 */
.arrow{width:13px; height:44px;
  clip-path:polygon(0 0,100% 50%,0 100%,0 68%,52% 50%,0 32%)}
.arrow:nth-child(1){background:var(--ink)}
.arrow:nth-child(2){background:var(--sage)}
.arrow:nth-child(3){background:var(--sage-light)}
/* Reversed, for ink grounds */
.mark--rev .arrow:nth-child(1){background:var(--white)}
.mark--rev .arrow:nth-child(2){background:var(--sage)}
.mark--rev .arrow:nth-child(3){background:var(--sage-light)}

/* ── Motion. TWO SURFACES, ONE IDEA. ───────────────────────────────────── */
/* The idea is unchanged: a move, then stillness. Nothing bounces, ever.
   What changes is the TEMPO, and it changes because the two surfaces are used
   differently.

   THE TILL is worked by muscle memory during a rush. Motion there is feedback,
   it must never make anyone wait, and the 200ms entrance ceiling still holds.
   Nothing in this block applies to it.

   THE WEBSITE is read, not operated. Demetri, 2026-09-17: "smooth scrolling,
   luxury feeling, smooth slow loading and graceful hover and scrolling effects
   ... slower than what you would do by default." So web motion is deliberately
   unhurried: a long settle rather than a quick snap.

   These tokens live HERE and nowhere else. They were declared inline in
   index.html until 2026-09-17, which is the same bug as a stray hex code — a
   second page picked up the colours and none of the motion, so sales.html
   loaded with no reveal at all while looking like it shared a design system. */
:root{
  /* Entrances. Near-instant launch, very long settle — the shape that reads as
     considered rather than slow. A longer duration on a SNAPPIER curve just
     feels sluggish; the curve is what buys the luxury, the duration only
     sustains it. */
  --ease-out:      cubic-bezier(.16,1,.3,1);
  /* Colour and border. Not motion, no drama, no overshoot. */
  --ease-standard: cubic-bezier(.4,0,.2,1);

  --dur-reveal: 900ms;   /* was 420ms */
  --dur-hover:  320ms;   /* was 140ms */
  --dur-focus:  220ms;   /* was 120ms */
  --dur-press:  140ms;   /* stays quick: a press is an ANSWER, and a slow one
                            reads as a dropped tap rather than as elegance */

  --reveal-travel: 24px; /* was 16px */
  --reveal-scale: .985;  /* settles INTO place rather than sliding into it.
                            Scale only, never a shadow or a lift. */
  --stagger-step: 140ms; /* was 90ms. Capped at six items — see the observer. */
}

/* Smooth scrolling site-wide, with room under a jump so an anchor never lands
   jammed against the top edge. */
html{scroll-behavior:smooth; scroll-padding-block-start:var(--s6,56px)}

/* SCROLL REVEAL, shared by every page.
   Content is in the DOM and tab-reachable from load — only opacity and
   transform hide it, never display or visibility, or a keyboard user would tab
   into nothing. */
.reveal{opacity:0;
  transform:translateY(var(--reveal-travel)) scale(var(--reveal-scale));
  transition:opacity var(--dur-reveal) var(--ease-out),
             transform var(--dur-reveal) var(--ease-out)}
.reveal.in{opacity:1; transform:none}

/* REDUCED MOTION MUST FORCE THE END STATE, not merely cancel the transition.
   Cancelling alone leaves every .reveal stuck at opacity:0 — the page would be
   BLANK for exactly the people who asked for less movement. That is the trap in
   a global `* { animation: none }` kill switch, and it is why this block sets
   the finished values explicitly. */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .reveal{opacity:1 !important; transform:none !important; transition:none !important}
  *,*::before,*::after{transition:none !important; animation:none !important}
}

@keyframes advance{
  0%{transform:translateX(0)}
  9%{transform:translateX(9px)}
  26%,100%{transform:translateX(0)}
}
.mark--live .arrow{animation:advance 1.8s cubic-bezier(.33,0,.15,1) infinite}
.mark--live .arrow:nth-child(2){animation-delay:.05s}
.mark--live .arrow:nth-child(3){animation-delay:.1s}

@keyframes enter{from{transform:translateY(6px);opacity:0}to{transform:none;opacity:1}}

@media (prefers-reduced-motion:reduce){ *{animation:none !important} }

/* ── Layout: full-bleed band, centered column ────────────────────────────── */
.band{background:var(--white)}
.band--alt{background:var(--paper)}
.band--ink{background:var(--ink); color:var(--white)}
.band > .inner{max-width:var(--page); margin:0 auto; padding:var(--s6) var(--s5)}
/* min(300px,100%): a bare 300px floor is wider than a 320px phone less its
   16px gutters, and the page scrolled sideways by 8px. */
.grid{display:grid; gap:var(--s3); grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr))}

/* ── The card — THE ONLY CONTAINER ──────────────────────────────────────── */
/* Depth comes from the border and the band behind it. No elevation scale, no
   blur, no translucency. A card inside a card is a layout mistake — use a
   hairline divider. */
.card{background:var(--white); border:var(--rule); border-radius:0;
  padding:var(--s4); box-shadow:none}

/* ── Buttons and links ──────────────────────────────────────────────────── */
/* 19px BOLD, not 15px semibold: white on sage is 4.23:1, which clears AA only
   as LARGE text (18.66px and 700). At 15px/600 every button on the site was
   under the floor (cold-eyes review, 2026-09-23). The colours are the brand's
   and stay; the size is what makes them legal. */
.btn{background:var(--sage); color:var(--white); padding:12px 20px;
  font-weight:700; border:0; border-radius:0; font-family:var(--font-body);
  font-size:19px; cursor:pointer; display:inline-block; text-decoration:none;
  min-height:var(--tap); line-height:24px}
/* HOVER IS CONTEXT-AWARE, and it has to be.
   `background:var(--ink)` is correct on a white or paper band -- sage darkens
   to ink, which reads as a press. On an INK band it is invisible: the button
   becomes the background and the control vanishes under the cursor. That was
   live on every button on the site, because all three sit on ink.
   On ink, hover goes LIGHTER. Same gesture, opposite direction. */
.btn{transition:background var(--dur-2,160ms) var(--ease,cubic-bezier(.33,0,.15,1)),
                color var(--dur-2,160ms) var(--ease,cubic-bezier(.33,0,.15,1)),
                transform var(--dur-1,120ms) var(--ease,cubic-bezier(.33,0,.15,1))}
.btn:hover,.btn:focus-visible{background:var(--ink)}
.band--ink .btn:hover,.band--ink .btn:focus-visible{background:var(--sage-light);color:var(--ink)}
.btn:active{transform:translateY(1px)}
/* On a phone the 19px label would wrap inside an inline button and read as a
   broken two-line box. Full width instead: one line, one obvious target. */
/* width:100% as well as block: a <button> does not stretch when made block,
   only a link does, so the form's submit stayed narrow beside the rest. */
@media(max-width:480px){.btn{display:block; width:100%; text-align:center; padding:12px var(--s2)}}
.btn--alt{background:none; border:var(--rule); padding:10px 18px; color:var(--ink)}
a{color:var(--link); text-decoration:none; border-bottom:2px solid var(--sage)}
a:hover,a:focus-visible{color:var(--ink)}
