/* Sable design tokens.
 *
 * Two palettes, lifted verbatim from the design document's sync(). Everything
 * else in the extension paints with --s-* only, so a theme or accent change is
 * one write to :root and nothing has to re-render.
 *
 * --s-accent and --s-tint are set from JS (theme.js), because they depend on
 * the chosen accent *and* the theme; the rest are static per theme.
 */

@import url('../../fonts/fonts.css');

:root {
  --s-bg: #f6f4ee;
  --s-surface: #fdfcf9;
  --s-raised: #f4f2ea;
  --s-panel: #fbfaf6;
  --s-line: #e2ded4;
  --s-hair: #f1efe6;
  --s-text: #1b1a17;
  --s-body: #3d3a2e;
  --s-muted: #8b8778;
  --s-faint: #b0ab99;
  --s-ink: #1b1a17;
  --s-inkText: #fdfcf9;
  --s-inkHover: #302e28;
  --s-field: #fdfcf9;
  --s-warn: #8a4230;
  --s-warnBg: #f9f1ec;
  --s-warnLine: #eddcd3;
  /* Paid. Sits at the same saturation and lightness as --s-warn so the two
   * read as a pair rather than a warning and a traffic light. Like the
   * warning, it never rotates with the accent — money that arrived is green
   * whichever colour the letterhead is. */
  --s-ok: #3d7a52;
  --s-shadow: 0 30px 60px -40px rgba(27, 26, 23, 0.28);
  --s-shadowCard: 0 24px 48px -32px rgba(27, 26, 23, 0.28);
  --s-shadowLift: 0 20px 40px -28px rgba(27, 26, 23, 0.32);

  /* Overwritten by theme.js on load; these are the olive defaults. */
  --s-accent: #6d6a30;
  --s-tint: #f1efdf;

  --s-serif: 'Bodoni Moda', 'Bodoni MT', Didot, Georgia, serif;
  --s-sans: Archivo, 'Segoe UI', system-ui, -apple-system, sans-serif;
  --s-mono: 'IBM Plex Mono', 'Cascadia Mono', Consolas, ui-monospace, monospace;

  --s-r-sm: 7px;
  --s-r-md: 9px;
  --s-r-lg: 11px;
  --s-r-xl: 14px;
}

:root[data-theme='dark'] {
  --s-bg: #131209;
  --s-surface: #1c1b14;
  --s-raised: #24221a;
  --s-panel: #1a1912;
  --s-line: #333025;
  --s-hair: #272419;
  --s-text: #f2efe4;
  --s-body: #ddd8c8;
  --s-muted: #9c9583;
  --s-faint: #6d6757;
  --s-ink: #f2efe4;
  --s-inkText: #131209;
  --s-inkHover: #ffffff;
  --s-field: #211f17;
  --s-warn: #d99177;
  --s-warnBg: #2a1c15;
  --s-warnLine: #41291f;
  --s-ok: #8ac9a0;
  --s-shadow: 0 30px 60px -34px rgba(0, 0, 0, 0.7);
  --s-shadowCard: 0 24px 48px -30px rgba(0, 0, 0, 0.66);
  --s-shadowLift: 0 20px 44px -26px rgba(0, 0, 0, 0.75);
}

/* The dark palette above is olive's, and it is the fallback for any accent
 * without a block of its own. It was never neutral — #131209 is a warm, very
 * dark olive — so picking Forest used to leave you reading green type on an
 * olive-black room. These blocks finish the job: every accent gets its own
 * black, rotated to that accent's hue at exactly the lightness and saturation
 * the olive ramp already used. Nothing here is eyeballed.
 *
 * Ink is the deliberate exception. It is the option someone picks when they
 * want no colour at all, so its ramp keeps the same lightnesses at a third of
 * the saturation — a black that is only just warm.
 *
 * The lightnesses are untouched, so contrast holds: body type stays between
 * 11.7:1 and 12.5:1 on the panel, against olive's 12.4:1. --s-warn is not in
 * here on purpose. A warning that turned green with the accent would stop
 * being a warning. */

:root[data-theme='dark'][data-ramp='forest'] {
  --s-bg: #091310;
  --s-surface: #141c1a;
  --s-raised: #1a2421;
  --s-panel: #121a18;
  --s-line: #25332f;
  --s-hair: #192723;
  --s-text: #e4f2ee;
  --s-body: #c8ddd7;
  --s-muted: #839c95;
  --s-faint: #576d67;
  --s-ink: #e4f2ee;
  --s-inkText: #091310;
  --s-field: #17211e;
}

:root[data-theme='dark'][data-ramp='clay'] {
  --s-bg: #130b09;
  --s-surface: #1c1614;
  --s-raised: #241c1a;
  --s-panel: #1a1412;
  --s-line: #332825;
  --s-hair: #271c19;
  --s-text: #f2e7e4;
  --s-body: #ddcdc8;
  --s-muted: #9c8983;
  --s-faint: #6d5c57;
  --s-ink: #f2e7e4;
  --s-inkText: #130b09;
  --s-field: #211917;
}

:root[data-theme='dark'][data-ramp='slate'] {
  --s-bg: #090c13;
  --s-surface: #14171c;
  --s-raised: #1a1d24;
  --s-panel: #12151a;
  --s-line: #252933;
  --s-hair: #191d27;
  --s-text: #e4e8f2;
  --s-body: #c8cfdd;
  --s-muted: #838b9c;
  --s-faint: #575e6d;
  --s-ink: #e4e8f2;
  --s-inkText: #090c13;
  --s-field: #171a21;
}

:root[data-theme='dark'][data-ramp='ink'] {
  --s-bg: #100f0d;
  --s-surface: #191917;
  --s-raised: #21201d;
  --s-panel: #171715;
  --s-line: #2e2d2a;
  --s-hair: #22211e;
  --s-text: #edece9;
  --s-body: #d6d4cf;
  --s-muted: #93918c;
  --s-faint: #65645f;
  --s-ink: #edece9;
  --s-inkText: #100f0d;
  --s-field: #1d1d1a;
}

@keyframes sb-shimmer {
  0% { background-position: -160% 0; }
  100% { background-position: 160% 0; }
}

@keyframes sb-rise {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}

@keyframes sb-blink {
  0%, 45% { opacity: 1; }
  55%, 100% { opacity: 0; }
}

@keyframes sb-flake {
  0% { transform: translate3d(0, 0, 0) rotate(0deg) scale(.6); opacity: 0; }
  12% { opacity: 1; }
  70% { opacity: 1; }
  100% { transform: translate3d(var(--dx, 0px), -300px, 0) rotate(var(--rot, 180deg)) scale(1); opacity: 0; }
}

@keyframes sb-seal {
  0% { transform: scale(.7); opacity: 0; }
  55% { transform: scale(1.06); }
  100% { transform: scale(1); opacity: 1; }
}

/* The sign-in sequence: the ring turns while Google is asked, the bar sweeps
 * under it, the seal's S wipes in, each restored line draws its rule, and the
 * finished panel folds down like paper settling. */

@keyframes sb-spin {
  to { transform: rotate(360deg); }
}

@keyframes sb-sweep {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(220%); }
}

@keyframes sb-draw {
  0% { width: 0; }
  100% { width: 100%; }
}

@keyframes sb-fold {
  0% { transform: translateY(14px) rotate(-2.5deg); opacity: 0; }
  100% { transform: none; opacity: 1; }
}

@keyframes sb-tick {
  0% { clip-path: inset(0 100% 0 0); }
  100% { clip-path: inset(0 0 0 0); }
}

/* The first screen. Loose invoices drift behind the card (--r keeps whatever
 * angle each one was laid down at), and the title arrives a word at a time
 * with the rule drawing itself underneath. */

@keyframes sb-float {
  0%, 100% { transform: translateY(0) rotate(var(--r, 0deg)); }
  50% { transform: translateY(-13px) rotate(calc(var(--r, 0deg) + 1.2deg)); }
}

@keyframes sb-word {
  0% { opacity: 0; transform: translateY(14px); }
  100% { opacity: 1; transform: none; }
}

@keyframes sb-rule {
  0% { transform: scaleX(0); }
  100% { transform: scaleX(1); }
}

/* A money field answering. The shake is for text it could not read — short
 * and small, because the field has already said so in words and this is only
 * pointing at where. The settle is the opposite: the number was understood
 * and rewritten, and the flush says the change was deliberate. */

@keyframes sb-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-3px); }
  40% { transform: translateX(3px); }
  60% { transform: translateX(-2px); }
  80% { transform: translateX(2px); }
}

/* The drop zone. Breathing while a file is held over it, one pulse as it
 * lands, and the logo settling into place once the background is cleared. */

@keyframes sb-breathe {
  0%, 100% { opacity: .2; transform: scale(.99); }
  50% { opacity: .6; transform: scale(1); }
}

@keyframes sb-take {
  0% { transform: scale(1); }
  40% { transform: scale(.975); }
  100% { transform: scale(1); }
}

@keyframes sb-settle-in {
  0% { opacity: 0; transform: scale(.94); }
  100% { opacity: 1; transform: none; }
}

@keyframes sb-settle {
  0% { border-color: var(--s-accent); background: var(--s-tint); }
  100% { border-color: var(--s-line); background: var(--s-field); }
}

/* ---- leaving, and moving between accounts -----------------------------
 *
 * The wax seal is the arrival gesture — it belongs to signing in, a moment
 * somebody earns. Leaving does not borrow it. The desk is cleared instead:
 * the accent drains out first because colour belongs to the account, the
 * workspace settles nine pixels and fades as though put down rather than
 * thrown away, and the wordmark rises into the space it left. The two states
 * overlap, so the panel is never blank in between.
 *
 * Switching accounts is one sheet passing upward with the next account behind
 * it. One object moving, not a crossfade between two screenshots. */

@keyframes sb-putaway {
  0% { transform: none; opacity: 1; }
  100% { transform: translateY(9px) scale(.985); opacity: 0; }
}

@keyframes sb-drain {
  0% { opacity: 1; }
  100% { opacity: 0; }
}

@keyframes sb-clear {
  0%, 55% { opacity: 0; }
  100% { opacity: 1; }
}

@keyframes sb-swapout {
  0% { transform: none; opacity: 1; }
  100% { transform: translateY(-7px); opacity: 0; }
}

@keyframes sb-swapin {
  0% { transform: translateY(7px); opacity: 0; }
  100% { transform: none; opacity: 1; }
}

@keyframes sb-wipe {
  0% { transform: translateY(101%); }
  46%, 54% { transform: none; }
  100% { transform: translateY(-101%); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ---- erasing an account ------------------------------------------------
 * Turn 12a. The ring is the *grace period*, not the erase: a last unhurried
 * second and a half in which nothing has happened yet and Cancel still works.
 * An erase cannot be undone halfway through, so the cancellable part is placed
 * before it rather than pretended over it.
 *
 * Durations live on the elements rather than here, because the first 1.85s is
 * a real window that JavaScript is also counting — the animation and the timer
 * have to be the same length or the ring would lie about when Cancel stops
 * working.
 */

/* The arc, filling clockwise. 207 is the circumference of r=33. */
@keyframes sb-erase { from { stroke-dashoffset: 207; } to { stroke-dashoffset: 0; } }

/* Cancel leaving is the signal that the erase has started. The point of no
 * return needs no caption. */
@keyframes sb-cancel { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(4px); } }

/* Only after Cancel has gone does anything move. Nothing is destroyed while a
 * cancel is still on screen. */
@keyframes sb-sheetgo {
  from { transform: none; opacity: .9; }
  to { transform: translateY(-18px) scale(.9); opacity: 0; }
}

/* The opposite of the wax seal: a breath outward, then inward to nothing. */
@keyframes sb-ringgo {
  0% { transform: scale(1); opacity: 1; }
  24% { transform: scale(1.07); opacity: 1; }
  100% { transform: scale(.7); opacity: 0; }
}

@keyframes sb-done { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: none; } }
