/* Ember - a Snapchat-Memories-shaped face on Immich. Built by EZ Dev.
   Snapchat's chrome is black with one saturated accent doing all the work.
   Everywhere Snapchat is yellow, Ember is the purple-red ember gradient. */

:root {
  --bg: #000000;
  --surface: #101014;
  --surface-2: #1c1c22;
  --surface-3: #2a2a32;
  --text: #ffffff;
  --text-dim: #8e8e93;
  --text-dimmer: #5a5a61;

  /* The single accent. Swap these three and the whole app rebrands. */
  --brand: #d62b58;
  --brand-purple: #8b2fd6;
  --brand-red: #f0453a;
  --grad: linear-gradient(135deg, var(--brand-purple) 0%, var(--brand) 55%, var(--brand-red) 100%);

  --radius-tile: 10px;
  --radius-pill: 999px;
  --gap: 3px;
  --nav-h: 58px;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  font-synthesis: none;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}

/* Scrollbars are hidden app-wide, per house rule. Scrolling still works by
   wheel, trackpad and touch -- only the bar itself is gone. */
* { scrollbar-width: none; -ms-overflow-style: none; }
*::-webkit-scrollbar { width: 0; height: 0; display: none; }

button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
img, video { display: block; max-width: 100%; }

/* ------------------------------------------------------------- splash ---- */

#splash {
  position: fixed; inset: 0; z-index: 100;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 18px; background: var(--bg);
  transition: opacity 420ms var(--ease), visibility 420ms;
}
#splash.gone { opacity: 0; visibility: hidden; }
.splash-mark { width: 104px; height: 104px; animation: markIn 620ms var(--ease) both; }
.splash-word {
  font-size: 30px; font-weight: 700; letter-spacing: 0.22em; text-indent: 0.22em;
  animation: fadeUp 620ms var(--ease) 140ms both;
}
.splash-by { font-size: 11px; letter-spacing: 0.16em; color: var(--text-dimmer);
  text-transform: uppercase; animation: fadeUp 620ms var(--ease) 260ms both; }
@keyframes markIn { from { opacity: 0; transform: scale(0.82); } to { opacity: 1; transform: none; } }
@keyframes fadeUp { from { opacity: 0; transform: translateY(9px); } to { opacity: 1; transform: none; } }

/* -------------------------------------------------------------- login ---- */

#login { position: fixed; inset: 0; z-index: 60; display: none;
  flex-direction: column; align-items: center; justify-content: center; gap: 14px; padding: 28px; }
#login.show { display: flex; }
#login .mark { width: 76px; height: 76px; margin-bottom: 6px; }
#login h1 { font-size: 26px; font-weight: 700; letter-spacing: 0.2em; text-indent: 0.2em; }
#login p.sub { color: var(--text-dim); font-size: 14px; margin-bottom: 14px; text-align: center; }
#login input {
  width: 100%; max-width: 340px; padding: 15px 18px; font-size: 16px;
  background: var(--surface-2); color: var(--text);
  border: 1.5px solid transparent; border-radius: var(--radius-pill); outline: none;
  transition: border-color 160ms var(--ease);
}
#login input:focus { border-color: var(--brand); }
#login input::placeholder { color: var(--text-dimmer); }
.btn-brand {
  width: 100%; max-width: 340px; padding: 15px 18px; font-size: 16px; font-weight: 700;
  border-radius: var(--radius-pill); background: var(--grad); color: #fff;
  transition: transform 120ms var(--ease), opacity 160ms;
}
.btn-brand:active { transform: scale(0.975); }
.btn-brand:disabled { opacity: 0.55; cursor: default; }
.err { color: var(--brand-red); font-size: 13.5px; min-height: 18px; text-align: center; }

/* --------------------------------------------------------------- shell ---- */

#app { display: none; flex-direction: column; height: 100%; }
#app.show { display: flex; }

.topbar {
  position: sticky; top: 0; z-index: 20; background: var(--bg);
  padding: 10px 14px 4px; display: flex; flex-direction: column; gap: 10px;
}
.topbar-row { display: flex; align-items: center; gap: 10px; }
.topbar h2 { font-size: 26px; font-weight: 700; letter-spacing: -0.02em; flex: 1; }
.icon-btn {
  width: 34px; height: 34px; border-radius: 50%; background: var(--surface-2);
  display: grid; place-items: center; flex: 0 0 auto;
  transition: background 140ms var(--ease), transform 120ms var(--ease);
}
.icon-btn:active { transform: scale(0.92); }
.icon-btn.on { background: var(--grad); }

/* Snapchat's filter row: pills, the active one filled with the accent. */
.tabs { display: flex; gap: 7px; overflow-x: auto; padding-bottom: 6px; }
.tab {
  flex: 0 0 auto; padding: 7px 15px; border-radius: var(--radius-pill);
  background: var(--surface-2); color: var(--text-dim);
  font-size: 13.5px; font-weight: 600; white-space: nowrap;
  transition: color 160ms var(--ease), background 160ms var(--ease);
}
.tab.active { color: #fff; background: var(--grad); }

.page { flex: 1; overflow-y: auto; padding-bottom: calc(var(--nav-h) + 14px); -webkit-overflow-scrolling: touch; }
.page[hidden] { display: none; }

/* --------------------------------------------------------- flashbacks ---- */

.flashbacks { padding: 6px 0 14px; }
.flashbacks h3, .section-label {
  font-size: 12px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--text-dim); padding: 0 14px 9px;
}
.flash-row { display: flex; gap: 10px; overflow-x: auto; padding: 0 14px 2px; }
.flash-card {
  flex: 0 0 auto; width: 124px; aspect-ratio: 3 / 4; border-radius: 16px;
  overflow: hidden; position: relative; background: var(--surface-2);
  transition: transform 160ms var(--ease);
}
.flash-card:active { transform: scale(0.965); }
.flash-card img { width: 100%; height: 100%; object-fit: cover; }
.flash-card .veil {
  position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(0,0,0,0.82) 0%, rgba(0,0,0,0.12) 45%, transparent 70%);
}
.flash-card .label {
  position: absolute; left: 11px; right: 11px; bottom: 10px;
  font-size: 13.5px; font-weight: 700; line-height: 1.22;
  text-shadow: 0 1px 5px rgba(0,0,0,0.6);
}
.flash-card .count { font-size: 11px; font-weight: 600; color: rgba(255,255,255,0.72); margin-top: 2px; }

/* ---------------------------------------------------------------- grid ---- */

.date-head {
  position: sticky; top: 0; z-index: 10;
  padding: 15px 14px 8px; font-size: 15px; font-weight: 700;
  background: linear-gradient(to bottom, var(--bg) 62%, transparent);
  display: flex; align-items: baseline; gap: 8px;
}
.date-head .dh-count { font-size: 12px; font-weight: 600; color: var(--text-dimmer); }

.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap); padding: 0 var(--gap); }

.tile {
  position: relative; aspect-ratio: 1; border-radius: var(--radius-tile);
  overflow: hidden; background: var(--surface-2); cursor: pointer;
  transition: transform 140ms var(--ease);
}
.tile:active { transform: scale(0.96); }
/* The thumbhash canvas sits under the real image and is revealed instantly, so
   a tile is never an empty grey box while its thumbnail loads. */
.tile canvas.hash { position: absolute; inset: 0; width: 100%; height: 100%; }
.tile img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity 260ms var(--ease);
}
.tile img.loaded { opacity: 1; }
.tile .dur {
  position: absolute; right: 6px; bottom: 5px; font-size: 11px; font-weight: 700;
  text-shadow: 0 1px 4px rgba(0,0,0,0.85); letter-spacing: 0.01em;
  /* Always white: this sits ON a photo, so it must not follow the theme's
     text colour. The light theme turned it black and unreadable. */
  color: #fff;
}
.tile .fav { position: absolute; left: 6px; bottom: 5px; width: 13px; height: 13px;
  filter: drop-shadow(0 1px 3px rgba(0,0,0,0.8)); }
.tile .check {
  position: absolute; top: 6px; right: 6px; width: 21px; height: 21px; border-radius: 50%;
  border: 2px solid rgba(255,255,255,0.85); background: rgba(0,0,0,0.22);
  display: none; place-items: center;
}
body.selecting .tile .check { display: grid; }
.tile.picked .check { background: var(--grad); border-color: transparent; }
.tile.picked img, .tile.picked canvas.hash { transform: scale(0.9); border-radius: var(--radius-tile); }
.tile.picked { outline: 2px solid var(--brand); outline-offset: -2px; }

/* -------------------------------------------------------------- viewer ---- */

#viewer {
  position: fixed; inset: 0; z-index: 80; background: #000;
  display: none; flex-direction: column;
  touch-action: none;
}
#viewer.show { display: flex; }
#viewer .stage { flex: 1; position: relative; display: grid; place-items: center; overflow: hidden; }
#viewer .stage img, #viewer .stage video { max-width: 100%; max-height: 100%; object-fit: contain; }

/* Story-style segment bar, the single most recognisable piece of Snapchat's
   viewer. One segment per memory in the day being viewed. */
.progress { position: absolute; top: 0; left: 0; right: 0; z-index: 3;
  display: flex; gap: 3px; padding: 9px 9px 0; }
.progress span { flex: 1; height: 2.5px; border-radius: 2px; background: rgba(255,255,255,0.3); overflow: hidden; }
.progress span.done { background: rgba(255,255,255,0.9); }
.progress span.now { background: var(--brand); }

.v-meta { position: absolute; top: 24px; left: 14px; z-index: 3; pointer-events: none;
  text-shadow: 0 1px 8px rgba(0,0,0,0.75); }
.v-meta .d { font-size: 16px; font-weight: 700; }
.v-meta .t { font-size: 12.5px; font-weight: 600; color: rgba(255,255,255,0.72); margin-top: 1px; }
.v-close { position: absolute; top: 20px; right: 12px; z-index: 4; width: 34px; height: 34px;
  border-radius: 50%; background: rgba(0,0,0,0.42); display: grid; place-items: center; }

/* Tap zones, exactly like a story: left third goes back, right two-thirds go
   forward. Invisible, above the media, below the buttons. */
.tapzone { position: absolute; top: 0; bottom: 0; z-index: 2; }
.tapzone.prev { left: 0; width: 32%; }
.tapzone.next { right: 0; width: 68%; }

.v-actions {
  display: flex; justify-content: space-around; align-items: center;
  padding: 14px 18px calc(14px + env(safe-area-inset-bottom, 0px));
  background: linear-gradient(to top, rgba(0,0,0,0.92), transparent);
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 4;
}
.v-act { display: flex; flex-direction: column; align-items: center; gap: 5px;
  font-size: 10.5px; font-weight: 600; color: rgba(255,255,255,0.86);
  transition: transform 120ms var(--ease); }
.v-act:active { transform: scale(0.9); }
.v-act svg { width: 23px; height: 23px; }
.v-act.on { color: var(--brand); }

/* ----------------------------------------------------------- bottom nav --- */

.nav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30; height: var(--nav-h);
  padding-bottom: env(safe-area-inset-bottom, 0px);
  display: grid; grid-template-columns: repeat(4, 1fr);
  background: rgba(10,10,12,0.92); backdrop-filter: blur(20px) saturate(150%);
  border-top: 0.5px solid rgba(255,255,255,0.09);
}
.nav button { display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; font-size: 10px; font-weight: 600; color: var(--text-dimmer);
  transition: color 160ms var(--ease); }
.nav button svg { width: 22px; height: 22px; }
.nav button.active { color: var(--brand); }

/* -------------------------------------------------------- search/people --- */

.searchbar { display: flex; align-items: center; gap: 9px; padding: 10px 14px;
  background: var(--surface-2); border-radius: var(--radius-pill); margin: 0 14px; }
.searchbar input { flex: 1; background: none; border: none; outline: none; color: var(--text); font-size: 15px; }
.searchbar input::placeholder { color: var(--text-dimmer); }
.people-row { display: flex; gap: 12px; overflow-x: auto; padding: 14px; }
.person { flex: 0 0 auto; width: 62px; text-align: center; }
.person img { width: 62px; height: 62px; border-radius: 50%; object-fit: cover; background: var(--surface-2); }
.person span { display: block; font-size: 11px; margin-top: 5px; color: var(--text-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* --------------------------------------------------------------- misc ----- */

.empty { padding: 64px 32px; text-align: center; color: var(--text-dim); }
.empty .mark { width: 62px; height: 62px; margin: 0 auto 18px; opacity: 0.5; }
.empty h3 { font-size: 17px; font-weight: 700; color: var(--text); margin-bottom: 7px; }
.empty p { font-size: 14px; line-height: 1.5; }

.settings { padding: 8px 14px; }
.set-row { display: flex; justify-content: space-between; align-items: center;
  padding: 15px 16px; background: var(--surface); border-radius: 13px; margin-bottom: 8px; font-size: 15px; }
.set-row .v { color: var(--text-dim); font-size: 13.5px; }
.set-row.tap { cursor: pointer; }
.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; margin-right: 7px; }
.dot.up { background: #34c759; } .dot.down { background: var(--brand-red); }

.spinner { width: 26px; height: 26px; margin: 34px auto; border-radius: 50%;
  border: 2.5px solid var(--surface-3); border-top-color: var(--brand);
  animation: spin 720ms linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

.selbar {
  position: fixed; left: 12px; right: 12px; bottom: calc(var(--nav-h) + 12px); z-index: 40;
  display: none; align-items: center; gap: 12px; padding: 11px 16px;
  background: var(--surface-2); border-radius: var(--radius-pill);
  box-shadow: 0 8px 28px rgba(0,0,0,0.55);
}
body.selecting .selbar { display: flex; }
.selbar .n { flex: 1; font-size: 14px; font-weight: 600; }
.selbar button { font-size: 14px; font-weight: 700; color: var(--brand); }

@media (min-width: 700px) {
  .grid { grid-template-columns: repeat(6, 1fr); }
  .page, .topbar { max-width: 1100px; margin-inline: auto; width: 100%; }
  .flash-card { width: 152px; }
}

/* Opening a memory should feel like Snapchat's snap-open, not a page swap. */
#viewer.show { animation: viewerIn 200ms var(--ease); }
@keyframes viewerIn { from { opacity: 0; transform: scale(1.035); } to { opacity: 1; transform: none; } }
#viewer .stage img, #viewer .stage video { animation: mediaIn 220ms var(--ease); }
@keyframes mediaIn { from { opacity: 0; } to { opacity: 1; } }

/* Search sits in the top bar on Memories, the way Snapchat does it. */
.topbar .searchbar { margin: 0; padding: 9px 13px; }
.clear-x { display: grid; place-items: center; width: 19px; height: 19px; border-radius: 50%;
  background: var(--surface-3); color: var(--text-dim); flex: 0 0 auto; }

/* My Eyes Only gate */
.vault-gate .pin {
  display: block; width: 190px; margin: 16px auto 4px; padding: 13px 16px;
  text-align: center; font-size: 21px; font-weight: 700; letter-spacing: 0.42em;
  text-indent: 0.42em; background: var(--surface-2); color: var(--text);
  border: 1.5px solid transparent; border-radius: var(--radius-pill); outline: none;
}
.vault-gate .pin:focus { border-color: var(--brand); }
.vault-gate .btn-brand { max-width: 190px; margin: 8px auto 0; }

/* [hidden] must beat the display rules that follow it. */
[hidden] { display: none !important; }

/* Login: Veri EZ is the front door, Immich the fallback underneath. */
.link-btn { font-size: 13.5px; font-weight: 600; color: var(--text-dim); padding: 6px 4px; }
.link-btn:active { color: var(--text); }
/* The Veri EZ credit lines are approved copy with deliberate casing
   ("Bouncer on duty: Veri EZ"). Do not uppercase them. */
.powered { font-size: 12px; letter-spacing: 0.01em; color: var(--text-dimmer); margin-top: 18px; }
.powered strong { color: var(--brand); font-weight: 700; }

/* ------------------------------------------------------------- brands ---- */
/* One codebase, two skins. EZ Pic keeps the ember gradient; JSnap is Snapchat
   yellow. Yellow needs dark text on top of it, which the gradient did not, so
   --on-brand exists rather than hard-coding white. */
:root { --on-brand: #ffffff; }

html[data-brand="jsnap"] {
  --brand: #fffc00;
  --brand-purple: #fffc00;
  --brand-red: #ffe600;
  --grad: linear-gradient(135deg, #fffc00 0%, #fff600 55%, #ffe600 100%);
  --on-brand: #000000;
}
html[data-brand="jsnap"] .err { color: #ff5a52; }        /* yellow can't carry an error */
html[data-brand="jsnap"] .dot.down { background: #ff5a52; }

.btn-brand { color: var(--on-brand); }
.tab.active { color: var(--on-brand); }
.tile.picked .check svg { stroke: var(--on-brand); }

/* The ghost draws in currentColor so it takes the brand colour; the camera
   carries its own gradient fill. */
.splash-mark, #login .mark, .empty .mark { color: var(--brand); }

/* Snapchat's chrome is heavier and rounder than the ember look. */
html[data-brand="jsnap"] .tab { font-weight: 700; }
html[data-brand="jsnap"] .topbar h2 { letter-spacing: -0.035em; }
html[data-brand="jsnap"] .splash-word,
html[data-brand="jsnap"] #lg-word { letter-spacing: 0.06em; text-indent: 0.06em; }

/* --------------------------------------------------------------- sweep ---- */
/* One card at a time. Left throws it away, right keeps it, down files it --
   Ethan's spec. The card follows the finger so the gesture is legible before
   it is committed. */
.nav { grid-template-columns: repeat(5, 1fr); }

.sweep-wrap { display: flex; flex-direction: column; height: 100%; align-items: center;
  justify-content: center; gap: 14px; padding: 6px 14px 0; }
.sweep-count { font-size: 12.5px; font-weight: 600; color: var(--text-dim); }

.sweep-card {
  position: relative; width: min(100%, 340px); aspect-ratio: 3 / 4;
  border-radius: 20px; overflow: hidden; background: var(--surface-2);
  box-shadow: 0 18px 46px rgba(0,0,0,0.55); touch-action: none; cursor: grab;
  will-change: transform;
}
.sweep-card:active { cursor: grabbing; }
.sweep-card img, .sweep-card video { width: 100%; height: 100%; object-fit: cover; }
.sweep-card.settling { transition: transform 220ms var(--ease), opacity 220ms var(--ease); }

/* Verdict stamps, revealed proportionally to the drag. */
.stamp {
  position: absolute; top: 18px; padding: 7px 14px; border-radius: var(--radius-pill);
  font-size: 15px; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase;
  opacity: 0; pointer-events: none; border: 2.5px solid currentColor;
}
.stamp.keep { right: 16px; color: #34c759; transform: rotate(9deg); }
.stamp.del { left: 16px; color: #ff453a; transform: rotate(-9deg); }
.stamp.file { left: 50%; bottom: 22px; top: auto; transform: translateX(-50%); color: var(--brand); }

.sweep-meta { font-size: 12.5px; color: var(--text-dim); text-align: center; }
.sweep-actions { display: flex; gap: 16px; align-items: center; justify-content: center; padding-top: 2px; }
.sw-btn { width: 54px; height: 54px; border-radius: 50%; display: grid; place-items: center;
  background: var(--surface-2); transition: transform 120ms var(--ease); }
.sw-btn:active { transform: scale(0.9); }
.sw-btn svg { width: 23px; height: 23px; }
.sw-btn.del { color: #ff453a; }
.sw-btn.keep { color: #34c759; }
.sw-btn.file { color: var(--brand); }
.sw-btn.undo { width: 42px; height: 42px; color: var(--text-dim); }
.sw-btn.undo svg { width: 18px; height: 18px; }
.sweep-hint { font-size: 11.5px; color: var(--text-dimmer); text-align: center; padding-bottom: 4px; }

/* Album sheet, opened by the downward swipe. */
.sheet-back { position: fixed; inset: 0; z-index: 70; background: rgba(0,0,0,0.62);
  display: none; align-items: flex-end; }
.sheet-back.show { display: flex; }
.sheet { width: 100%; max-height: 74vh; background: var(--surface); border-radius: 20px 20px 0 0;
  padding: 16px 14px calc(20px + env(safe-area-inset-bottom, 0px)); overflow-y: auto;
  animation: sheetUp 240ms var(--ease); }
@keyframes sheetUp { from { transform: translateY(26px); opacity: 0; } to { transform: none; opacity: 1; } }
.sheet h3 { font-size: 17px; font-weight: 700; margin-bottom: 4px; }
.sheet p.sub { font-size: 13px; color: var(--text-dim); margin-bottom: 14px; }
.album-row { display: flex; align-items: center; gap: 11px; padding: 12px 13px;
  background: var(--surface-2); border-radius: 13px; margin-bottom: 7px; width: 100%;
  font-size: 15px; text-align: left; }
.album-row .n { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.album-row .c { font-size: 12px; color: var(--text-dimmer); }
.sheet .newrow { display: flex; gap: 8px; margin-top: 10px; }
.sheet .newrow input { flex: 1; padding: 12px 15px; font-size: 15px; background: var(--surface-2);
  color: var(--text); border: 1.5px solid transparent; border-radius: var(--radius-pill); outline: none; }
.sheet .newrow input:focus { border-color: var(--brand); }
.sheet .newrow button { padding: 12px 18px; border-radius: var(--radius-pill);
  background: var(--grad); color: var(--on-brand); font-weight: 700; font-size: 14px; }
.sheet .cancel { display: block; width: 100%; text-align: center; padding: 14px 0 2px;
  color: var(--text-dim); font-size: 14.5px; font-weight: 600; }

/* ---------------------------------------------------- jsnap: light theme --- */
/* Snapchat's Memories is white, not dark. Yellow is an ACCENT here, never text:
   #fffc00 on white is unreadable, so active states use black type on a yellow
   fill rather than yellow type. */
html[data-brand="jsnap"] {
  --bg: #ffffff;
  --surface: #f4f4f6;
  --surface-2: #ebebef;
  --surface-3: #dcdce2;
  --text: #000000;
  --text-dim: #6a6a72;
  --text-dimmer: #9b9ba3;
  --on-brand: #000000;
}
html[data-brand="jsnap"] body { background: #fff; color: #000; }

/* Chrome that assumed a dark ground */
html[data-brand="jsnap"] .nav {
  background: rgba(255,255,255,0.94);
  border-top: 0.5px solid rgba(0,0,0,0.12);
}
html[data-brand="jsnap"] .nav button.active { color: #000; }
html[data-brand="jsnap"] .nav button.active svg { color: #000; }
/* The active tab reads as a yellow pill with black type, the way Snap does it. */
html[data-brand="jsnap"] .tab.active { background: var(--brand); color: #000; }
html[data-brand="jsnap"] .date-head {
  background: linear-gradient(to bottom, #fff 62%, transparent);
}
html[data-brand="jsnap"] .tile { background: var(--surface-2); }
html[data-brand="jsnap"] #splash { background: #fff; }
html[data-brand="jsnap"] .btn-brand { background: var(--brand); color: #000; }
html[data-brand="jsnap"] .selbar { box-shadow: 0 8px 28px rgba(0,0,0,0.18); }
html[data-brand="jsnap"] .sheet-back { background: rgba(0,0,0,0.35); }
html[data-brand="jsnap"] .sweep-card { box-shadow: 0 18px 46px rgba(0,0,0,0.22); }
html[data-brand="jsnap"] .icon-btn.on { background: var(--brand); color: #000; }
html[data-brand="jsnap"] .v-act.on { color: #b8a800; }   /* yellow needs darkening on white */
html[data-brand="jsnap"] .selbar button { color: #000; }
html[data-brand="jsnap"] .set-row .v .dot.up { background: #1e9e4a; }
html[data-brand="jsnap"] .powered strong { color: #8a7f00; }
html[data-brand="jsnap"] .progress span.now { background: #000; }
html[data-brand="jsnap"] .progress span { background: rgba(0,0,0,0.22); }
html[data-brand="jsnap"] .progress span.done { background: rgba(0,0,0,0.6); }
/* The mark carries its own white/black/yellow; don't tint it with --brand. */
html[data-brand="jsnap"] .splash-mark,
html[data-brand="jsnap"] #login .mark { color: inherit; }

/* The viewer stays black in both brands -- photos need a dark surround. */
html[data-brand="jsnap"] #viewer { background: #000; }
html[data-brand="jsnap"] #viewer .v-meta { color: #fff; }

/* ------------------------------------------- jsnap: the real Memories grid --- */
/* Measured from Snapchat screenshots Ethan sent 2026-08-19. Four columns,
   flush, portrait, square-cornered -- not the spaced rounded squares EZ Pics
   uses. */
html[data-brand="jsnap"] .grid {
  grid-template-columns: repeat(4, 1fr);
  gap: 0;
  padding: 0;
}
html[data-brand="jsnap"] .tile {
  aspect-ratio: 3 / 4;
  border-radius: 0;
}
html[data-brand="jsnap"] .tile:active { transform: none; opacity: .85; }
/* Snap puts the duration bottom-LEFT, plain white, no chip. */
html[data-brand="jsnap"] .tile .dur { left: 6px; right: auto; font-weight: 600; }
html[data-brand="jsnap"] .tile .fav { left: auto; right: 6px; }

/* Tabs are underlined text, not pills. */
html[data-brand="jsnap"] .tabs { gap: 20px; padding: 2px 14px 0; }
html[data-brand="jsnap"] .tab {
  background: none; padding: 6px 0 9px; font-size: 19px; font-weight: 700;
  color: #9b9ba3; border-radius: 0; position: relative;
}
html[data-brand="jsnap"] .tab.active { background: none; color: #000; }
html[data-brand="jsnap"] .tab.active::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0;
  height: 3px; background: #000; border-radius: 2px;
}

/* No sticky day headers -- Snap has none in the grid. */
html[data-brand="jsnap"] .date-head { display: none; }

/* Instead: a floating month pill on the right while scrolling. */
.scroll-date {
  position: fixed; right: 0; z-index: 25;
  background: #fa1e4e; color: #fff; font-weight: 700; font-size: 15px;
  padding: 9px 18px; border-radius: 4px 0 0 4px;
  opacity: 0; transform: translateX(8px);
  transition: opacity 180ms var(--ease), transform 180ms var(--ease);
  pointer-events: none; letter-spacing: .01em;
}
.scroll-date.show { opacity: 1; transform: none; }

/* Header: big title, search glyph, grey Select pill. */
html[data-brand="jsnap"] .topbar h2 { font-size: 28px; }
html[data-brand="jsnap"] .icon-btn { background: var(--surface-2); color: #000; }
html[data-brand="jsnap"] .searchbar { background: var(--surface-2); }

/* The month pill is a grab handle, not just a readout -- with a decade of
   memories you need to be able to scrub. */
.scroll-date {
  pointer-events: auto;
  cursor: grab;
  touch-action: none;
  user-select: none; -webkit-user-select: none;
  /* Bigger hit area than it looks: a 9px-tall pill is not a touch target. */
  padding: 12px 20px 12px 22px;
  box-shadow: -2px 2px 10px rgba(0,0,0,0.22);
}
.scroll-date:active, .scroll-date.grabbed { cursor: grabbing; }
.scroll-date.grabbed {
  transform: scale(1.06);
  box-shadow: -3px 3px 16px rgba(0,0,0,0.32);
}

/* The last row of tiles was running under the nav bar. The grid has no gutter
   on JSnap, so the page itself has to hold the clearance. */
.page { padding-bottom: calc(var(--nav-h) + 34px + env(safe-area-inset-bottom, 0px)); }
html[data-brand="jsnap"] .page {
  padding-bottom: calc(var(--nav-h) + 42px + env(safe-area-inset-bottom, 0px));
}
/* And give the nav a real edge so content never appears to touch it. */
html[data-brand="jsnap"] .nav {
  background: #fff;
  box-shadow: 0 -6px 14px rgba(255,255,255,0.9);
}

/* --------------------------------------------- jsnap: flashback cards ----- */
/* Snapchat shows flashbacks as big two-across tiles with the label in white
   over the image -- "Flashback from August 20" -- not small carousel chips. */
html[data-brand="jsnap"] .flashbacks { padding: 0 0 2px; }
html[data-brand="jsnap"] .flashbacks h3 { display: none; }
html[data-brand="jsnap"] .flash-row {
  display: grid; grid-auto-flow: column;
  grid-auto-columns: calc(50% - 1px);
  gap: 2px; padding: 0; overflow-x: auto; scroll-snap-type: x mandatory;
}
html[data-brand="jsnap"] .flash-card {
  width: auto; aspect-ratio: 3 / 4; border-radius: 0; scroll-snap-align: start;
}
html[data-brand="jsnap"] .flash-card .veil {
  background: linear-gradient(to top, rgba(0,0,0,0.72) 0%, rgba(0,0,0,0.10) 38%, transparent 60%);
}
html[data-brand="jsnap"] .flash-card .label {
  left: 14px; right: 14px; bottom: 14px; font-size: 17px; font-weight: 700;
  color: #fff; line-height: 1.2;
}
html[data-brand="jsnap"] .flash-card .count { display: none; }

.person span.unnamed { color: var(--brand); font-weight: 600; }
html[data-brand="jsnap"] .person span.unnamed { color: #8a7f00; }

/* Bottom nav: the buttons were sitting high because the safe-area inset was all
   padding BELOW them, so they looked off-centre in the white strip. Split it so
   they sit centred in the visible bar, and give the grid real clearance. */
.nav {
  height: auto;
  padding-top: calc(env(safe-area-inset-bottom, 0px) * 0.32);
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) * 0.68);
  min-height: var(--nav-h);
}
.nav button { padding: 9px 0 9px; }
.page { padding-bottom: calc(var(--nav-h) + 52px + env(safe-area-inset-bottom, 0px)); }
html[data-brand="jsnap"] .page {
  padding-bottom: calc(var(--nav-h) + 60px + env(safe-area-inset-bottom, 0px));
}

/* ------------------------------------------------ clean up: first run ----- */
.sweep-intro { padding: 34px 26px 40px; max-width: 460px; margin: 0 auto; text-align: left; }
.sweep-intro .mark { width: 46px; height: 46px; color: var(--brand); display: block; margin-bottom: 14px; }
.sweep-intro h3 { font-size: 22px; font-weight: 700; margin-bottom: 8px; }
.sweep-intro .lede { color: var(--text-dim); font-size: 14.5px; line-height: 1.5; margin-bottom: 18px; }
.sweep-intro .lede.small { font-size: 13px; margin: 18px 0 22px; }
.intro-row { display: flex; gap: 12px; align-items: flex-start; margin-bottom: 13px; font-size: 14.5px; line-height: 1.45; }
.intro-row .k {
  flex: 0 0 92px; font-weight: 700; font-size: 13px; padding: 5px 0;
  border-radius: 7px; text-align: center;
}
.intro-row .k.del  { color: #ff453a; background: rgba(255,69,58,0.12); }
.intro-row .k.keep { color: #34c759; background: rgba(52,199,89,0.12); }
.intro-row .k.file { color: var(--brand); background: rgba(214,43,88,0.12); }
html[data-brand="jsnap"] .intro-row .k.file { color: #8a7f00; background: rgba(255,252,0,0.28); }
.sweep-intro .btn-brand { max-width: 100%; margin-top: 4px; }

/* Face ID is the primary door where the device supports it. */
.btn-face {
  width: 100%; max-width: 340px; padding: 13px 18px; font-size: 15px; font-weight: 600;
  border-radius: var(--radius-pill); background: var(--surface-2); color: var(--text);
  display: flex; align-items: center; justify-content: center; gap: 9px;
  transition: transform 120ms var(--ease);
}
.btn-face:active { transform: scale(0.975); }
html[data-brand="jsnap"] .btn-face { background: var(--surface-2); color: #000; }

/* ------------------------------------------------------------- import ----- */
.nav { grid-template-columns: repeat(6, 1fr); }
.nav button { font-size: 9.5px; }
.nav button svg { width: 20px; height: 20px; }

.import-wrap { padding: 22px 20px 40px; max-width: 520px; margin: 0 auto; }
.import-wrap h3 { font-size: 21px; font-weight: 700; margin-bottom: 8px; }
.import-wrap .lede { color: var(--text-dim); font-size: 14.5px; line-height: 1.55; margin-bottom: 18px; }
.import-wrap .lede.small { font-size: 12.5px; margin: 10px 0 0; }
.import-wrap .err-text { color: var(--brand-red); }

.step { display: flex; gap: 13px; margin-bottom: 16px; }
.step .n {
  flex: 0 0 26px; height: 26px; border-radius: 50%; background: var(--grad);
  color: var(--on-brand); font-weight: 700; font-size: 13px;
  display: grid; place-items: center;
}
.step strong { display: block; font-size: 15px; margin-bottom: 3px; }
.step p { color: var(--text-dim); font-size: 13.5px; line-height: 1.5; margin-bottom: 5px; }
.step p.warn {
  color: var(--text); background: rgba(240,69,58,0.10);
  border-left: 3px solid var(--brand-red); padding: 8px 11px; border-radius: 0 7px 7px 0;
  font-size: 13px;
}
.mono { font-family: ui-monospace, Menlo, monospace; font-size: 0.94em; }

.dropzone {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 9px;
  padding: 28px 18px; margin: 8px 0 14px; cursor: pointer;
  border: 2px dashed var(--surface-3); border-radius: 15px;
  color: var(--text-dim); font-size: 14.5px; font-weight: 600;
  transition: border-color 160ms var(--ease), color 160ms var(--ease);
}
.dropzone:hover { border-color: var(--brand); color: var(--text); }
.fileline {
  display: flex; justify-content: space-between; gap: 10px; padding: 11px 14px;
  background: var(--surface); border-radius: 11px; margin-bottom: 7px; font-size: 14px;
}
.fileline .v { color: var(--text-dim); }

.bar { height: 8px; border-radius: 999px; background: var(--surface-2); overflow: hidden; margin: 6px 0; }
.bar span { display: block; height: 100%; background: var(--grad); transition: width 300ms var(--ease); }
html[data-brand="jsnap"] .bar span { background: var(--brand); }
html[data-brand="jsnap"] .step .n { background: var(--brand); color: #000; }

/* ------------------------------------------------- feels-like-an-app ------ */
/* A browser gives away that it is a browser through a handful of specific
   behaviours. Each rule below kills one of them. */

/* 1. The ~300ms tap delay, and double-tap-to-zoom on every control. */
button, .tile, .tab, .nav button, .flash-card, .album-row, .person, .set-row,
.dropzone, .scroll-date, .sweep-card { touch-action: manipulation; }

/* 2. Long-press text selection and the iOS callout menu on chrome. Media and
      real text stay selectable. */
body { -webkit-touch-callout: none; }
.topbar, .nav, .tabs, .v-actions, .sweep-actions, .selbar { user-select: none; -webkit-user-select: none; }

/* 3. Rubber-band overscroll showing the page behind the app. */
html, body { overscroll-behavior: none; }
.page { overscroll-behavior-y: contain; -webkit-overflow-scrolling: touch; }

/* 4. Native-feeling press feedback instead of a hover state that never fires. */
.set-row.tap:active, .album-row:active, .fileline:active { transform: scale(0.985); opacity: 0.9; }
.set-row.tap, .album-row, .fileline { transition: transform 90ms var(--ease), opacity 90ms var(--ease); }

/* 5. Pages fade in rather than snapping, so nav feels like a transition. */
.page:not([hidden]) { animation: pageIn 180ms var(--ease); }
@keyframes pageIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

/* 6. Respect people who ask for less motion. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* 7. Tiles fade in from their thumbhash instead of popping. */
.tile img { transition: opacity 220ms var(--ease); }

/* 8. Keep the status-bar area painted in standalone mode. */
@supports (padding-top: env(safe-area-inset-top)) {
  .topbar { padding-top: calc(10px + env(safe-area-inset-top, 0px)); }
}

/* ===== Shared feedback widget (colors only) =====
   Doors, modal and photo attach all live in _shared/branding/feedback.{css,js}
   (built 2026-08-20). Ethan's rule: same behaviour in every app, only the
   color changes — so this is EZ Pics's entire share of it.

   Everything is stated explicitly because the shared file's fallback chain
   looks for --accent-ice/--surface-alt/--text-muted/--radius/--shadow-lg/
   --danger, and this app has none of those names (it has --brand, --surface-2,
   --text-dim, --radius-pill, --brand-red). Without this block the widget would
   render on guessed defaults.                                                */
:root {
  --ezfb-accent: var(--brand);
  --ezfb-accent-2: var(--brand-purple);
  --ezfb-surface: var(--surface);
  --ezfb-surface-alt: var(--surface-2);
  --ezfb-border: rgba(255,255,255,0.09);   /* this app has no --border token */
  --ezfb-text: var(--text);
  --ezfb-text-muted: var(--text-dim);
  --ezfb-text-faint: var(--text-dimmer);
  --ezfb-danger: var(--brand-red);
  --ezfb-error-bg: rgba(240,69,58,0.12);
  --ezfb-error-text: var(--brand-red);
  --ezfb-radius: var(--radius-tile);
  --ezfb-radius-sm: var(--radius-pill);    /* buttons/inputs here are pills */
  --ezfb-btn-bg: var(--brand);
  --ezfb-btn-bg-hover: var(--brand);
  --ezfb-btn-text: var(--on-brand);
}

/* The primary button in this app is the ember GRADIENT, not a flat fill — a
   flat --brand button would read as a different app's UI. Gradients can't be
   expressed through the shared --ezfb-btn-bg variable, so this is the one
   structural override EZ Pics makes. Works for JSnap too: --grad there is the
   near-flat yellow ramp, and --on-brand flips the label to black. */
.ezfb-btn { background: var(--grad); }
.ezfb-btn:hover { filter: brightness(1.06); }

/* JSnap is a LIGHT theme, which the shared widget's dark-theme assumptions
   don't survive on their own. Two things need re-stating:

   1. #fffc00 on a near-white surface is effectively invisible — the icons
      disappeared entirely when the brand accent was passed straight through.
      Both doors get darkened yellows here (still unmistakably the brand, just
      dark enough to read), which is the same call the .err override above
      makes when it says yellow can't carry an error.
   2. Borders and the modal backdrop are re-toned for light, since the shared
      defaults assume a dark page underneath. */
html[data-brand="jsnap"] {
  --ezfb-accent: #d4b800;     /* lightbulb — was invisible at full #fffc00 */
  --ezfb-accent-2: #a68f00;   /* question mark, a step darker so the pair reads */
  --ezfb-border: rgba(0,0,0,0.12);
  --ezfb-danger: #ff5a52;
  --ezfb-error-bg: rgba(255,90,82,0.12);
  --ezfb-error-text: #d33a33;
}
html[data-brand="jsnap"] .ezfb-overlay { background: rgba(0,0,0,.45); }

/* ------------------------------------------------- keep-your-own-copy ----- */
.terms-back {
  position: fixed; inset: 0; z-index: 95; background: rgba(0,0,0,0.72);
  display: flex; align-items: center; justify-content: center; padding: 22px;
  backdrop-filter: blur(6px);
}
.terms {
  background: var(--surface); border-radius: 18px; padding: 26px 24px;
  max-width: 420px; width: 100%; max-height: 88vh; overflow-y: auto;
}
.terms h3 { font-size: 20px; font-weight: 700; margin-bottom: 10px; }
.terms p { color: var(--text-dim); font-size: 14.5px; line-height: 1.55; margin-bottom: 12px; }
.terms ul { margin: 0 0 14px 18px; }
.terms li { color: var(--text-dim); font-size: 14px; line-height: 1.5; margin-bottom: 9px; }
.terms strong { color: var(--text); }
.terms .tiny { font-size: 12.5px; color: var(--text-dimmer); margin-bottom: 18px; }
.terms .btn-brand { max-width: 100%; }
.terms .btn-brand:disabled { opacity: 0.55; }
