/* ============================================================
   Discipline App - styles.css (the LOOK)

   Version 2 is a deliberate simplification. The old screen was a stack
   of bordered boxes; this one leans on space and type size instead, so
   there's less to look at and the important thing (what's left to do
   today) is the thing your eye lands on.

   Everything that used to clutter the main screen — the add-task
   options — now lives in a sheet that slides up only when needed.
   ============================================================ */


/* ---------- 1. Design tokens ----------
   Change a colour once here and it changes everywhere.

   THE SYSTEM. The app is dressed as the interface from Solo Leveling: cold
   blue-violet windows, cyan hairlines, corner brackets, uppercase tracked
   labels. One deliberate departure from the show — DONE STAYS GREEN. The
   System has no green anywhere, but on a checklist that colour is doing real
   work, and "did I finish?" should be answerable at arm's length without
   reading a word. Cyan is the interface; green is the achievement.          */
:root {
  --bg:        #05070E;   /* page background: deep blue-black */
  --raised:    #0C1524;   /* windows sitting on the background */
  --raised-2:  #13203A;   /* inputs, chips */
  --line:      #1D3050;   /* hairline borders */
  --text:      #DDE8FA;
  --dim:       #6E86AC;   /* secondary text */
  --accent:    #7CF57C;   /* the "done" green — kept on purpose */
  --system:    #4FD2FF;   /* the System's cyan: borders, glow, headings */
  --system-2:  #8C6BFF;   /* the violet in the gradient */
  --danger:    #FF6B7A;   /* a failed quest, a demotion */
  --amber:     #F5C86B;   /* injured status */
  --radius:    4px;       /* the System has corners, not pillows */
}

/* The glow that makes a panel look projected rather than printed. */
@keyframes system-in {
  from { opacity: 0; transform: translateY(8px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}


/* ---------- 2. Base ---------- */
* { box-sizing: border-box; margin: 0; padding: 0; }

body {
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
               "Helvetica Neue", Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}

button, input { font-family: inherit; font-size: 1rem; color: inherit; }

/* One reusable "make this disappear". app.js adds/removes this class. */
.hidden { display: none !important; }

/* Tabular numbers keep digits the same width, so a changing number
   doesn't shuffle the text beside it. */
.streak-count, .progress-text, .card-streak { font-variant-numeric: tabular-nums; }


/* ---------- 3. Shared buttons ---------- */
.btn-primary {
  background: var(--accent);
  color: #0B0F14;
  border: none;
  border-radius: 14px;
  padding: 15px;
  font-size: 15px;
  font-weight: 700;
  width: 100%;
  cursor: pointer;
}
.btn-primary:active { transform: scale(0.985); }

.btn-ghost {
  background: none;
  color: var(--dim);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 15px;
  width: 100%;
  cursor: pointer;
}

.btn-quiet {
  background: none;
  border: none;
  color: var(--dim);
  font-size: 13px;
  padding: 14px;
  width: 100%;
  cursor: pointer;
}

.link-btn {
  background: none;
  border: none;
  color: inherit;
  font-size: 12px;
  text-decoration: underline;
  cursor: pointer;
  padding: 0;
}

.field {
  background: var(--raised-2);
  border: 1px solid transparent;   /* transparent, not none, so focus doesn't shift layout */
  border-radius: 13px;
  padding: 14px;
  outline: none;
  width: 100%;
  transition: border-color 0.15s ease;
}
.field:focus { border-color: #37505f; }
.field::placeholder { color: #55616F; }

/* Hide the tiny spinner arrows browsers add to number inputs. */
.field-mins::-webkit-outer-spin-button,
.field-mins::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }


/* ---------- 4. Lock screen ---------- */
.lock {
  position: fixed;
  inset: 0;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  z-index: 100;
}
.lock-card { width: 100%; max-width: 300px; text-align: center; }
.lock-tick {
  width: 56px; height: 56px;
  margin: 0 auto 20px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--bg);
  display: flex; align-items: center; justify-content: center;
  font-size: 28px; font-weight: 700;
}
.lock-title { font-size: 26px; font-weight: 800; letter-spacing: -0.4px; }
.lock-sub { color: var(--dim); font-size: 13px; margin-top: 6px; }
.lock-form { display: flex; flex-direction: column; gap: 10px; margin-top: 24px; }
.lock-error { color: #F58C8C; font-size: 13px; margin-top: 12px; }


/* ---------- 5. Page layout ----------
   One vertical column: header, scrolling list, footer pinned at the
   bottom. 100dvh accounts for the phone's address bar sliding away;
   env(safe-area-inset-*) keeps content clear of the notch/home bar.  */
/* The app is now the STAGE the two views slide across, so the padding moved
   onto .view and this just holds the space. */
.app {
  position: relative;
  height: 100dvh;
  max-width: 540px;
  margin: 0 auto;
  overflow: hidden;
}


/* ---------- 6. Header ----------
   No box, no border. Just a big number — the simplification that does
   the most work on this screen.                                      */
.header { flex-shrink: 0; }

.streak { display: flex; align-items: baseline; gap: 10px; }
.streak-count { font-size: 54px; font-weight: 800; line-height: 1; letter-spacing: -2px; }
.streak-label { font-size: 14px; color: var(--dim); }

.header-meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  margin-top: 10px;
  font-size: 12.5px;
  color: var(--dim);
}
.best-line { flex-shrink: 0; }

.sick-banner {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  margin-top: 16px;
  padding: 10px 14px;
  border-radius: 12px;
  background: rgba(245, 200, 107, 0.12);
  color: var(--amber);
  font-size: 12.5px;
}


/* ---------- 7. The list ---------- */
.list-section {
  flex: 1;          /* take the leftover height... */
  overflow-y: auto; /* ...and scroll inside itself */
  min-height: 0;    /* required for scrolling inside a flex child */
  margin-top: 26px;
}

.list-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}
.progress-text { font-size: 12.5px; color: var(--dim); }

/* A dot that quietly reports whether we're talking to the server. */
.sync-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0.5;
  transition: background 0.3s ease, opacity 0.3s ease;
}
.sync-dot.busy   { opacity: 1; animation: pulse 1s ease-in-out infinite; }
.sync-dot.offline{ background: var(--amber); opacity: 1; animation: none; }

.progress-track {
  height: 3px;
  background: var(--raised-2);
  border-radius: 999px;
  overflow: hidden;
  margin-bottom: 18px;
}
.progress-fill {
  height: 100%;
  width: 0%;
  background: var(--accent);
  border-radius: 999px;
  transition: width 0.35s ease;
}

.task-list { list-style: none; display: flex; flex-direction: column; gap: 8px; }

.task {
  display: flex;
  align-items: flex-start;
  gap: 13px;
  background: var(--raised);
  border-radius: var(--radius);
  padding: 15px 16px;
  transition: opacity 0.2s ease;
}
.task.done { opacity: 0.4; }

/* The round tick. */
.tick {
  flex-shrink: 0;
  width: 25px; height: 25px;
  margin-top: 1px;
  border-radius: 50%;
  border: 2px solid #33404F;
  background: transparent;
  display: flex; align-items: center; justify-content: center;
  font-size: 13px;
  color: var(--bg);
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.task.done .tick { background: var(--accent); border-color: var(--accent); }

.task-body { flex: 1; min-width: 0; }
.task-name { font-size: 15px; line-height: 1.35; word-break: break-word; }
.task.done .task-name { text-decoration: line-through; }

/* The little grey labels under a task name. */
.meta { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 7px; }
.tag {
  font-size: 10.5px;
  color: var(--dim);
  background: var(--raised-2);
  border-radius: 999px;
  padding: 3px 9px;
}
.tag.physical { color: var(--amber); background: rgba(245, 200, 107, 0.1); }

/* Either/or tasks: two tappable choices instead of one tick. */
.options { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.option {
  flex: 1;
  min-width: 120px;
  background: var(--raised-2);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 10px 12px;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.option:active { transform: scale(0.985); }
.option-name { font-size: 13.5px; display: block; }
.option-mins { font-size: 10.5px; color: var(--dim); display: block; margin-top: 3px; }
/* The one he actually did. */
.option.chosen {
  border-color: var(--accent);
  background: rgba(124, 245, 124, 0.09);
}

.delete {
  flex-shrink: 0;
  background: none; border: none;
  color: #4A5766;
  font-size: 19px; line-height: 1;
  padding: 4px 2px 4px 8px;
  cursor: pointer;
}

.empty-state {
  text-align: center;
  color: var(--dim);
  font-size: 14px;
  line-height: 1.8;
  padding: 48px 0;
}


/* ---------- 8. Footer ---------- */
.footer { flex-shrink: 0; padding-top: 14px; }


/* ---------- 9. The add-task sheet ----------
   Anchored to the bottom of the screen because that's where thumbs are. */
.sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(6, 9, 13, 0.7);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  animation: fade-in 0.2s ease;
}

.sheet {
  width: 100%;
  max-width: 540px;
  background: var(--raised);
  border-radius: 26px 26px 0 0;
  padding: 12px 20px calc(env(safe-area-inset-bottom) + 22px);
  animation: slide-up 0.28s cubic-bezier(0.2, 0.9, 0.3, 1);
}

/* The little grab handle that signals "this panel slides". */
.sheet-grip {
  width: 38px; height: 4px;
  border-radius: 999px;
  background: #35404E;
  margin: 0 auto 18px;
}

.sheet-title { font-size: 17px; font-weight: 700; margin-bottom: 16px; }

.row { display: flex; gap: 8px; margin-bottom: 10px; }
.field-name { flex: 1; min-width: 0; }
.field-mins { width: 82px; flex-shrink: 0; text-align: center; }

.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
.chip {
  background: var(--raised-2);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 9px 15px;
  font-size: 12.5px;
  color: var(--dim);
  cursor: pointer;
  transition: all 0.15s ease;
}
/* aria-pressed is both the accessibility state AND our styling hook, so
   there's only one source of truth for "is this switch on?". */
.chip[aria-pressed="true"] {
  background: rgba(124, 245, 124, 0.12);
  border-color: var(--accent);
  color: var(--accent);
}
.chip#physicalChip[aria-pressed="true"] {
  background: rgba(245, 200, 107, 0.12);
  border-color: var(--amber);
  color: var(--amber);
}

.sheet-hint { font-size: 11.5px; color: var(--dim); line-height: 1.6; margin-top: 14px; }

.sheet-actions { display: flex; gap: 10px; margin-top: 18px; }
.sheet-actions > * { flex: 1; }


/* ---------- 10. Popups ---------- */
.overlay {
  position: fixed;
  inset: 0;
  z-index: 70;
  background: rgba(6, 9, 13, 0.8);
  backdrop-filter: blur(6px);
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
  animation: fade-in 0.2s ease;
}

.card {
  background: var(--raised);
  border-radius: 24px;
  padding: 32px 24px 24px;
  width: 100%; max-width: 330px;
  text-align: center;
  animation: pop-in 0.35s cubic-bezier(0.2, 1.3, 0.4, 1);
}
.card-tick {
  width: 64px; height: 64px;
  margin: 0 auto 18px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--bg);
  display: flex; align-items: center; justify-content: center;
  font-size: 32px; font-weight: 700;
}
.card-tick.amber {
  background: rgba(245, 200, 107, 0.14);
  color: var(--amber);
}
.card-title { font-size: 23px; font-weight: 800; letter-spacing: -0.4px; }
.card-sub { margin-top: 10px; font-size: 13.5px; line-height: 1.65; color: var(--dim); }
.card-sub strong { color: var(--text); }
.card-streak { margin: 18px 0 20px; font-size: 13px; color: var(--accent); font-weight: 600; }
.button-row { display: flex; gap: 10px; margin-top: 22px; }
.button-row > * { flex: 1; }


/* ---------- 11. Toast ---------- */
.toast {
  position: fixed;
  left: 50%;
  bottom: calc(env(safe-area-inset-bottom) + 24px);
  transform: translateX(-50%);
  z-index: 90;
  background: var(--raised-2);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 12px 18px;
  font-size: 13px;
  max-width: calc(100vw - 40px);
  text-align: center;
  animation: fade-in 0.2s ease;
}


/* ---------- 10b. Hub and pages ----------
   Two screens stacked in the same space. The hub is always there; a page
   slides over it from the right and the hub drifts left a little behind it,
   which is the gesture language every phone already uses for "deeper in". */

.view {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  padding: calc(env(safe-area-inset-top) + 18px) 20px calc(env(safe-area-inset-bottom) + 14px);
  transition: transform 0.34s cubic-bezier(0.22, 0.9, 0.28, 1), opacity 0.24s ease;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.view-page {
  transform: translateX(100%);
  visibility: hidden;
  background: var(--bg);
  z-index: 20;
}

/* Set on #app while a page is open. */
.app.page-open #hub {
  transform: translateX(-16%);
  opacity: 0.35;
  pointer-events: none;
}
.app.page-open .view-page {
  transform: translateX(0);
  visibility: visible;
}

/* ---- the cards ---- */
.cards { display: flex; flex-direction: column; gap: 10px; margin-top: 18px; flex: 1; }

.card-row {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  text-align: left;
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px 18px;
  color: var(--text);
  cursor: pointer;
  transition: transform 0.12s ease, border-color 0.2s ease;
}
.card-row:active { transform: scale(0.985); }
.card-row.all-done { border-color: #2F5A3A; }

.card-main { flex: 1; min-width: 0; }
.card-title { font-size: 15.5px; font-weight: 700; }
.card-sub { font-size: 12.5px; color: var(--dim); margin-top: 3px; }

/* The count on the right: the number that tells you whether to tap. */
.card-count {
  flex-shrink: 0;
  font-size: 15px;
  font-weight: 700;
  color: var(--dim);
}
.card-count.has-left { color: var(--text); }
.card-row.all-done .card-count { color: var(--accent); }
.card-chevron { flex-shrink: 0; color: #465363; font-size: 20px; line-height: 1; }

/* A hairline of progress along the bottom of a card. */
.card-bar { height: 2px; background: var(--line); border-radius: 2px; margin-top: 10px; overflow: hidden; }
.card-bar > div { height: 100%; background: var(--accent); border-radius: 2px; transition: width 0.3s ease; }

/* ---- page furniture ---- */
.page-head { display: flex; align-items: flex-start; gap: 10px; flex-shrink: 0; margin-bottom: 4px; }
.page-heading { flex: 1; min-width: 0; }
.page-title { font-size: 21px; font-weight: 800; letter-spacing: -0.2px; }
.page-sub { font-size: 12.5px; color: var(--dim); margin-top: 3px; }

.back-btn {
  background: none;
  border: none;
  color: var(--text);
  font-size: 30px;
  line-height: 1;
  padding: 0 6px 0 0;
  margin-top: -4px;
  cursor: pointer;
}
.back-btn:active { opacity: 0.6; }

.page-body { flex: 1; display: flex; flex-direction: column; min-height: 0; padding-top: 12px; }

/* ---- the cadence picker in the add sheet ---- */
.cadence-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.cadence {
  flex: 1;
  min-width: 62px;
  background: none;
  border: 1px solid var(--line);
  border-radius: 11px;
  color: var(--dim);
  font-size: 12.5px;
  padding: 9px 4px;
  cursor: pointer;
}
.cadence[aria-checked="true"] {
  background: rgba(124, 245, 124, 0.12);
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 700;
}


/* ---------- 11b. The Tell-Jarvis chat ----------
   The same sheet as "New task", but the middle of it is a conversation. The
   log scrolls on its own so the input never leaves the thumb's reach, and it's
   capped in height so the phone keyboard can't push it off screen. */
.footer-row { display: flex; gap: 10px; }
.footer-row > * { flex: 1; }

/* On the Jarvis page the chat fills the height it's given, with the input
   pinned underneath it. */
.chat-panel { display: flex; flex-direction: column; flex: 1; min-height: 0; }

.chat-log {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 2px 0 12px;
}

.bubble {
  max-width: 84%;
  padding: 10px 13px;
  border-radius: 16px;
  font-size: 14px;
  line-height: 1.5;
  animation: pop-in 0.18s ease;
  overflow-wrap: anywhere;
}

/* His words: the app's green, so the eye can follow the conversation. */
.bubble.you {
  align-self: flex-end;
  background: var(--accent);
  color: #0B0F14;
  border-bottom-right-radius: 5px;
  font-weight: 600;
}

/* Jarvis's: quiet, sitting on the sheet. */
.bubble.jarvis {
  align-self: flex-start;
  background: #1D2530;
  border: 1px solid var(--line);
  border-bottom-left-radius: 5px;
}

/* A turn that actually changed the list gets the green edge. */
.bubble.jarvis.did { border-color: #2F5A3A; }

.bubble.thinking { color: var(--dim); animation: pulse 1.2s ease-in-out infinite; }

.chat-form { display: flex; gap: 8px; align-items: center; }
.chat-form .field { flex: 1; min-width: 0; }

.chat-send {
  flex-shrink: 0;
  width: 46px; height: 46px;
  border-radius: 14px;
  border: none;
  background: var(--accent);
  color: #0B0F14;
  font-size: 19px;
  font-weight: 700;
  cursor: pointer;
}
.chat-send:disabled { opacity: 0.4; cursor: default; }
.chat-send:active { transform: scale(0.95); }


/* ---------- 12. Animations ---------- */
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes slide-up { from { transform: translateY(100%); } to { transform: translateY(0); } }
@keyframes pop-in {
  from { opacity: 0; transform: scale(0.86) translateY(10px); }
  to   { opacity: 1; transform: scale(1) translateY(0); }
}
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } }

/* Respect people who've asked their phone to reduce motion. */
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}


/* ==========================================================================
   13. THE SYSTEM
   --------------------------------------------------------------------------
   Everything above is the plain app. Everything below dresses it as the
   System from Solo Leveling, and it is deliberately kept in one block at the
   end so the whole look can be removed in one go if it ever wears thin.

   The rule the show follows, and so does this: the interface is COLD and
   quiet, and the drama is saved for the moments that earn it — a quest
   completed, a level gained, a rank lost.
   ========================================================================== */

/* A projected window: cold panel, cyan hairline, two corner brackets on the
   diagonal. Two rather than four — enough to read as a frame, quiet enough to
   sit behind a list looked at every morning. */
.sys {
  position: relative;
  background: linear-gradient(160deg, rgba(19, 32, 58, 0.92), rgba(8, 14, 26, 0.92));
  border: 1px solid rgba(79, 210, 255, 0.28);
  border-radius: var(--radius);
  box-shadow: 0 0 0 1px rgba(79, 210, 255, 0.05), 0 8px 30px rgba(4, 10, 24, 0.7),
              inset 0 1px 0 rgba(140, 200, 255, 0.06);
}
.sys::before, .sys::after {
  content: "";
  position: absolute;
  width: 13px; height: 13px;
  border: 2px solid var(--system);
  opacity: 0.75;
  pointer-events: none;
}
.sys::before { top: -1px; left: -1px; border-right: 0; border-bottom: 0; }
.sys::after  { bottom: -1px; right: -1px; border-left: 0; border-top: 0; }

/* Section labels in the System voice: small, spaced, cyan. */
.sys-label {
  font-size: 10.5px;
  letter-spacing: 2.4px;
  text-transform: uppercase;
  color: var(--system);
  opacity: 0.9;
}

/* ---- THE STATUS WINDOW -------------------------------------------------- */
.status {
  padding: 15px 16px 14px;
  margin-bottom: 14px;
  animation: system-in 0.3s ease;
}
.status-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.status-name { font-size: 13px; letter-spacing: 3px; text-transform: uppercase; color: var(--text); }
.status-level { font-size: 13px; letter-spacing: 2px; color: var(--system); font-weight: 700; }

.status-rank { display: flex; align-items: center; gap: 11px; margin-top: 10px; }

/* The rank letter in a diamond — the badge the show stamps on a gate. */
.rank-badge {
  width: 30px; height: 30px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  font-size: 13px;
  font-weight: 800;
  color: var(--system);
  border: 1.5px solid var(--system);
  transform: rotate(45deg);
  box-shadow: 0 0 14px rgba(79, 210, 255, 0.25);
}
.rank-badge > span { transform: rotate(-45deg); }
.rank-note { font-size: 11.5px; color: var(--dim); line-height: 1.5; }

/* EXP: the bar that only ever fills. */
.exp-row { margin-top: 12px; }
.exp-head { display: flex; justify-content: space-between; font-size: 10.5px; letter-spacing: 2px; color: var(--dim); text-transform: uppercase; }
.exp-track {
  height: 6px;
  margin-top: 6px;
  background: rgba(79, 210, 255, 0.1);
  border: 1px solid rgba(79, 210, 255, 0.18);
  overflow: hidden;
}
/* Today's part-finished day, riding on the end of the earned bar. Lighter and
   translucent, because it is not banked until the day is complete. */
.exp-today {
  position: absolute;
  top: 0;
  height: 100%;
  background: linear-gradient(90deg, rgba(140, 107, 255, 0.55), rgba(79, 210, 255, 0.55));
  transition: width 0.4s ease, left 0.4s ease;
}
.exp-track { position: relative; }

.exp-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--system-2), var(--system));
  box-shadow: 0 0 12px rgba(79, 210, 255, 0.55);
  transition: width 0.45s cubic-bezier(0.2, 0.9, 0.3, 1);
}

/* STR / INT — honest counters, started the day the System came online. */
.stats { display: flex; gap: 18px; margin-top: 13px; }
.stat { flex: 1; }
.stat-name { font-size: 10px; letter-spacing: 2px; color: var(--dim); }
.stat-value { font-size: 15px; font-weight: 700; color: var(--text); margin-top: 1px; }

/* ---- Quest cards -------------------------------------------------------- */
.card-row {
  background: linear-gradient(160deg, rgba(19, 32, 58, 0.9), rgba(8, 14, 26, 0.9));
  border: 1px solid rgba(79, 210, 255, 0.22);
  border-radius: var(--radius);
  padding: 14px 16px;
}
.card-row.all-done { border-color: rgba(124, 245, 124, 0.45); }
.card-title { font-size: 12px; letter-spacing: 2.4px; text-transform: uppercase; color: var(--system); }
.card-row.all-done .card-title { color: var(--accent); }
.card-sub { color: var(--dim); font-size: 12px; }
.card-count { font-variant-numeric: tabular-nums; }
.card-bar { background: rgba(79, 210, 255, 0.12); border-radius: 0; }
.card-bar > div { background: linear-gradient(90deg, var(--system-2), var(--system)); }
.card-row.all-done .card-bar > div { background: var(--accent); }
.card-chevron { color: rgba(79, 210, 255, 0.5); }

/* ---- Quest rows --------------------------------------------------------- */
.task { border-radius: var(--radius); border: 1px solid rgba(79, 210, 255, 0.14); }
.task.done { border-color: rgba(124, 245, 124, 0.3); }
.tick { border-radius: 3px; border-color: rgba(79, 210, 255, 0.45); }
.option { border-radius: 3px; }
.tag { border-radius: 2px; letter-spacing: 1px; text-transform: uppercase; font-size: 9.5px; }

/* ---- Page furniture ----------------------------------------------------- */
.page-title { letter-spacing: 3px; text-transform: uppercase; font-size: 16px; color: var(--system); }
.back-btn { color: var(--system); }

/* ---- Buttons ------------------------------------------------------------ */
.btn-primary {
  background: linear-gradient(180deg, rgba(79, 210, 255, 0.22), rgba(79, 210, 255, 0.1));
  border: 1px solid var(--system);
  border-radius: var(--radius);
  color: #DFF6FF;
  letter-spacing: 2px;
  text-transform: uppercase;
  font-size: 12.5px;
  box-shadow: 0 0 18px rgba(79, 210, 255, 0.18);
}
.btn-ghost, .chip, .cadence {
  border-radius: var(--radius);
  border-color: rgba(79, 210, 255, 0.3);
  letter-spacing: 1.4px;
  text-transform: uppercase;
  font-size: 11.5px;
}
.chip[aria-pressed="true"], .cadence[aria-checked="true"] {
  background: rgba(79, 210, 255, 0.14);
  border-color: var(--system);
  color: var(--system);
}
.chip#physicalChip[aria-pressed="true"] {
  border-color: var(--amber);
  color: var(--amber);
  background: rgba(245, 200, 107, 0.12);
}
.field { border-radius: var(--radius); background: var(--raised-2); border-color: rgba(79, 210, 255, 0.22); }
.sheet { border-radius: 8px 8px 0 0; border-top: 1px solid rgba(79, 210, 255, 0.3); }
.sheet-title { letter-spacing: 2.4px; text-transform: uppercase; color: var(--system); font-size: 13px; }

/* ---- The header --------------------------------------------------------- */
.streak-count { color: var(--text); text-shadow: 0 0 26px rgba(79, 210, 255, 0.35); }
.streak-label, .header-meta { letter-spacing: 1.6px; text-transform: uppercase; font-size: 10.5px; }

/* ---- Moments: the parts allowed to be loud ------------------------------ */
.overlay .card {
  border-radius: var(--radius);
  border: 1px solid var(--system);
  background: linear-gradient(160deg, rgba(20, 34, 62, 0.97), rgba(6, 11, 22, 0.97));
  box-shadow: 0 0 50px rgba(79, 210, 255, 0.28);
  position: relative;
}

/* The line the System says before anything else. */
.sys-heading {
  font-size: 11px;
  letter-spacing: 4px;
  text-transform: uppercase;
  color: var(--system);
  margin-bottom: 10px;
  opacity: 0.9;
}

/* LEVEL UP — the one genuinely triumphant moment. */
.levelup-level {
  font-size: 46px;
  font-weight: 800;
  color: var(--system);
  line-height: 1;
  margin: 6px 0 2px;
  text-shadow: 0 0 34px rgba(79, 210, 255, 0.6);
}

/* A demotion is stated, not shouted. Red edge, same calm window. */
.overlay .card.penalty { border-color: var(--danger); box-shadow: 0 0 44px rgba(255, 107, 122, 0.22); }
.penalty .sys-heading { color: var(--danger); }
.penalty-rank { font-size: 26px; font-weight: 800; letter-spacing: 3px; color: var(--danger); margin: 6px 0; }

/* Reprieve: standing a day down on purpose. */
.reprieve-left { font-size: 11px; letter-spacing: 1.6px; text-transform: uppercase; color: var(--dim); margin-top: 10px; }
.sick-banner { border-radius: var(--radius); letter-spacing: 1.4px; text-transform: uppercase; font-size: 10.5px; }

/* ---- The five attributes ------------------------------------------------ */
.stats { display: block; margin-top: 13px; }
.stat { margin-top: 7px; }
.stat-head { display: flex; justify-content: space-between; align-items: baseline; }
.stat-name { font-size: 10px; letter-spacing: 2px; color: var(--dim); }
.stat-value { font-size: 12px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.stat-track {
  height: 3px;
  margin-top: 3px;
  background: rgba(79, 210, 255, 0.1);
  overflow: hidden;
}
.stat-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--system-2), var(--system));
  transition: width 0.4s ease;
}
.cleared {
  margin-top: 12px;
  font-size: 10px;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--dim);
}
.stat-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.stat-row .cadence { min-width: 52px; }
.tag.stat { color: var(--system); border-color: rgba(79, 210, 255, 0.4); }


/* ==========================================================================
   14. THE GATE — the way in
   --------------------------------------------------------------------------
   A gate opens and you pass inward through it. Two things are true of it at
   once, which is why it earns its place instead of being a splash screen:

     • It is the LOADING STATE. The app fetches the list on launch, and this
       covers that moment, so the first thing seen is deliberate rather than a
       cached screen being swapped out underneath you.
     • It never makes the app slower than it is. It holds for a beat so it
       cannot flash, then leaves the instant the real data is ready.

   The exit is the whole idea: the gate rushes past you (scaling up and out
   of frame) while the app arrives from slightly behind (scaling up to its
   true size). Together they read as forward motion INTO something, not as
   one screen being replaced by another.
   ========================================================================== */

.boot {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 22px;
  background: radial-gradient(circle at 50% 45%, #0A1730 0%, #04070F 60%, #02040A 100%);
}

/* ---- THE GATE, from the actual reference --------------------------------
   I built two wrong ones before looking properly at the source. For the
   record, so nobody re-derives them: the gates in Solo Leveling are

     ROUND, not a vertical tear.
     WHITE-HOT IN THE MIDDLE, not dark. The light comes from inside it.
     SOFT-EDGED, with no rim at all — it fades into haze.
     SWIRLING. Spiral arms of blue turning around the core. That motion is
       the single most recognisable thing about them, and both of my previous
       attempts had none of it.

   So: a whirlpool of light seen head-on. Two conic gradients turning at
   different speeds and directions, heavily blurred so the bands read as arms
   rather than pie slices, a blazing core on top, and a wide blue haze behind
   holding it all in the dark.                                             */

.boot-gate {
  position: relative;
  width: 236px;
  height: 236px;
  animation: gate-appear 1s cubic-bezier(0.16, 0.9, 0.28, 1) both;
}

/* The atmosphere it sits in — wide, soft, no edge of its own. */
.rift {
  position: absolute;
  inset: -34%;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
    rgba(120, 200, 255, 0.30) 0%,
    rgba(52, 120, 220, 0.22) 34%,
    rgba(20, 60, 140, 0.14) 56%,
    rgba(8, 24, 66, 0) 76%);
  filter: blur(12px);
  animation: haze-breathe 6s ease-in-out infinite;
}

/* Spiral arms. The mask fades both the middle and the rim so the bands never
   show a hard boundary — which is what stops it looking like a pie chart. */
.rift-edge,
.rift-core {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  -webkit-mask-image: radial-gradient(circle, transparent 12%, #000 34%, #000 68%, transparent 88%);
  mask-image: radial-gradient(circle, transparent 12%, #000 34%, #000 68%, transparent 88%);
}

.rift-edge {
  background: conic-gradient(from 0deg,
    rgba(10, 40, 96, 0) 0deg,
    rgba(70, 160, 240, 0.80) 46deg,
    rgba(205, 240, 255, 0.95) 96deg,
    rgba(46, 118, 205, 0.55) 152deg,
    rgba(10, 40, 96, 0) 208deg,
    rgba(96, 185, 250, 0.75) 292deg,
    rgba(10, 40, 96, 0) 360deg);
  filter: blur(11px);
  animation: swirl 14s linear infinite;
}

/* A second, faster layer turning the other way. Two speeds is what makes it
   churn instead of merely spin. */
.rift-core {
  background: conic-gradient(from 140deg,
    rgba(10, 40, 96, 0) 0deg,
    rgba(150, 215, 255, 0.65) 60deg,
    rgba(255, 255, 255, 0.80) 120deg,
    rgba(60, 140, 225, 0.40) 190deg,
    rgba(10, 40, 96, 0) 260deg,
    rgba(130, 205, 255, 0.55) 320deg,
    rgba(10, 40, 96, 0) 360deg);
  filter: blur(16px);
  opacity: 0.85;
  animation: swirl 9s linear infinite reverse;
}

/* The blazing middle. This is the part the eye actually lands on. */
.wisp {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 126px;
  height: 126px;
  margin: -63px 0 0 -63px;
  border-radius: 50%;
  background: radial-gradient(circle,
    #FFFFFF 0%,
    #FFFFFF 22%,
    rgba(232, 248, 255, 0.96) 34%,
    rgba(158, 216, 255, 0.72) 50%,
    rgba(80, 165, 240, 0.34) 68%,
    rgba(40, 110, 200, 0) 84%);
  filter: blur(6px);
  animation: core-glow 3.4s ease-in-out infinite;
}
/* Only one core is wanted; the spare markup nodes stay out of the way. */
.w2, .w3, .w4 { display: none; }

@keyframes swirl {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
@keyframes haze-breathe {
  0%, 100% { transform: scale(1);    opacity: 0.9; }
  50%      { transform: scale(1.07); opacity: 1; }
}
@keyframes core-glow {
  0%, 100% { transform: scale(0.96); opacity: 0.94; }
  50%      { transform: scale(1.06); opacity: 1; }
}

/* It arrives already turning, and settles. */
@keyframes gate-appear {
  0%   { transform: scale(0.24) rotate(-90deg); opacity: 0; }
  55%  { opacity: 1; }
  100% { transform: scale(1) rotate(0deg); opacity: 1; }
}

/* ---- Going through ------------------------------------------------------
   The swirl accelerates, then the core blooms out and takes the screen. You
   go into the light — which is the only way through a gate that is brightest
   at its centre. */
.boot.opening { animation: gate-fade 0.85s cubic-bezier(0.45, 0, 0.25, 1) forwards; pointer-events: none; }
.boot.opening .boot-gate { animation: gate-rush 0.85s cubic-bezier(0.45, 0, 0.2, 1) forwards; }
.boot.opening .rift-edge { animation: swirl 0.7s linear infinite; }
.boot.opening .rift-core { animation: swirl 0.45s linear infinite reverse; }
.boot.opening .wisp { animation: core-bloom 0.85s cubic-bezier(0.4, 0, 0.2, 1) forwards; }
.boot.opening .boot-label,
.boot.opening .boot-status { animation: gate-recede 0.26s ease forwards; }

@keyframes gate-rush {
  0%   { transform: scale(1); }
  20%  { transform: scale(0.9); }        /* winds up */
  100% { transform: scale(4.2); }
}
@keyframes core-bloom {
  0%   { transform: scale(1); opacity: 1; }
  45%  { transform: scale(3.4); opacity: 1; }
  100% { transform: scale(11); opacity: 0; filter: blur(26px); }
}
@keyframes gate-fade {
  0%, 66% { opacity: 1; }
  100%    { opacity: 0; }
}
@keyframes gate-recede {
  to { opacity: 0; transform: translateY(8px); }
}

/* The app resolving out of the white. */
.app.arriving  { animation: arrive 0.85s cubic-bezier(0.2, 0.8, 0.25, 1) both; }
.lock.arriving { animation: arrive 0.7s cubic-bezier(0.2, 0.8, 0.25, 1) both; }

@keyframes arrive {
  0%   { opacity: 0; transform: scale(0.82); filter: blur(10px); }
  50%  { opacity: 1; }
  100% { opacity: 1; transform: scale(1); filter: blur(0); }
}


/* ---- Breaking the circle ------------------------------------------------
   A perfect disc reads as a graphic. In the source frames the gate is ragged:
   arcs sweep out past the body of it, and dark crackling veins run off into
   the air around it. Two additions, both purely decorative and both layered
   OVER the existing gate, so removing this section restores the clean vortex
   exactly.

   ARCS are partial rings — a circle with only one border edge coloured, so it
   draws a sweep rather than an outline. Each is bigger than the disc, tilted
   oddly, and turns at its own speed, which is what stops the silhouette ever
   settling into a circle.

   SPURS are the dark veins. Thin, tapered, bent slightly by a second segment,
   at angles deliberately chosen NOT to be evenly spaced — evenly spaced spikes
   look like a sun, and a gate is a wound. */

.gate-arc {
  position: absolute;
  top: 50%;
  left: 50%;
  border-radius: 50%;
  border: 2px solid transparent;
  pointer-events: none;
  filter: blur(2.5px);
}
/* Different sizes, different arcs lit, different speeds — never in step. */
.arc1 {
  width: 300px; height: 300px; margin: -150px 0 0 -150px;
  border-top-color: rgba(150, 220, 255, 0.55);
  border-right-color: rgba(96, 180, 250, 0.25);
  animation: arc-turn 17s linear infinite;
}
.arc2 {
  width: 268px; height: 268px; margin: -134px 0 0 -134px;
  border-left-color: rgba(120, 200, 255, 0.45);
  border-bottom-color: rgba(70, 150, 235, 0.18);
  animation: arc-turn 11s linear infinite reverse;
}
.arc3 {
  width: 340px; height: 340px; margin: -170px 0 0 -170px;
  border-bottom-color: rgba(90, 175, 245, 0.30);
  animation: arc-turn 25s linear infinite;
  opacity: 0.8;
}

@keyframes arc-turn {
  from { transform: rotate(0deg)   scaleY(0.94); }
  to   { transform: rotate(360deg) scaleY(0.94); }   /* squashed = not a circle */
}

/* The veins. Dark against the glow, the way they read in the frames. */
.gate-spur {
  position: absolute;
  top: 50%;
  left: 50%;
  height: 2px;
  transform-origin: 0 50%;
  background: linear-gradient(90deg,
    rgba(6, 14, 34, 0.85) 0%,
    rgba(8, 20, 48, 0.7) 45%,
    rgba(10, 26, 60, 0) 100%);
  filter: blur(1.4px);
  pointer-events: none;
}
/* A kink partway along, so no spur is a straight line. */
.gate-spur::after {
  content: "";
  position: absolute;
  right: -38%;
  top: 0;
  width: 44%;
  height: 100%;
  background: inherit;
  transform: rotate(14deg);
  transform-origin: 0 50%;
}

/* Angles picked to be uneven on purpose. */
.sp1 { width: 132px; transform: rotate(-24deg)  translateX(96px); animation: vein 6.5s ease-in-out infinite; }
.sp2 { width: 104px; transform: rotate(58deg)   translateX(104px); animation: vein 8.2s ease-in-out infinite 0.6s; }
.sp3 { width: 152px; transform: rotate(141deg)  translateX(92px);  animation: vein 7.1s ease-in-out infinite 1.4s; }
.sp4 { width: 88px;  transform: rotate(-118deg) translateX(110px); animation: vein 9.4s ease-in-out infinite 0.2s; }
.sp5 { width: 118px; transform: rotate(-71deg)  translateX(88px);  animation: vein 5.8s ease-in-out infinite 2.1s; }

/* They twitch rather than pulse — a crack settling, not a light breathing. */
@keyframes vein {
  0%, 100% { opacity: 0.85; }
  38%      { opacity: 0.45; }
  52%      { opacity: 0.95; }
  70%      { opacity: 0.6; }
}

/* The haze underneath loses its perfect roundness too. */
.rift { animation: haze-breathe 6s ease-in-out infinite, haze-warp 13s ease-in-out infinite; }

@keyframes haze-warp {
  0%, 100% { border-radius: 50% 50% 50% 50% / 50% 50% 50% 50%; }
  30%      { border-radius: 56% 44% 48% 52% / 46% 54% 46% 54%; }
  60%      { border-radius: 44% 56% 54% 46% / 55% 45% 55% 45%; }
}

/* On the way through, the ragged bits are flung outward and fade before the
   core blooms — otherwise they would still be hanging there over the app. */
.boot.opening .gate-arc { animation: arc-fling 0.55s ease-in forwards; }
.boot.opening .gate-spur { animation: vein-fade 0.4s ease-in forwards; }

@keyframes arc-fling {
  to { transform: rotate(90deg) scale(2.4); opacity: 0; }
}
@keyframes vein-fade {
  to { opacity: 0; }
}

/* Someone who has asked their phone to calm down gets none of this: the gate
   simply isn't shown (app.js checks the same query before animating). */
@media (prefers-reduced-motion: reduce) {
  .boot, .boot.opening { animation: none; }
  .boot-gate, .rift, .rift-edge, .rift-core, .wisp,
  .gate-arc, .gate-spur { animation: none; }
  .app.arriving, .lock.arriving { animation: none; }
}
