/* ============================================================
   neo Calc — styles.css   (single stylesheet by design)

   One file, not split: CSS has no import/cycle cost like the JS modules,
   and splitting would move the cascade order into fragile <link> ordering
   + a larger precache surface. This map keeps it navigable instead —
   search the banner text to jump to a section.

   CONTENTS (in source order)
     · Design tokens ...................... :root  (dark palette)
     · Header & Navigation ................ "Header & Navigation"
     · Install pill ....................... "Install pill"
     · Layout Architecture ................ "Layout Architecture"
     · Displays (calc input/result) ....... "Displays"
     · Buttons & Grids (all keypads) ...... "Buttons & Grids"
     · Converter View ..................... "Converter View"
     · Currency footer (ECB staleness) .... "Currency-only footer"
     · Percentage View .................... "Percentage View"
     · Base / Programmer View ............. "Base converter view"
     · Form view (Loan / Date&time / BMI) . "Form view"
     · Help view (user guide accordion) ... "Help view"
     · History slide-in panel ............. "History slide-in panel"
     · Landscape Overrides ................ "Landscape Overrides"
     · Portrait layout fixes .............. "Portrait layout fixes"
     · Chrome: brand / menu §C / modal §D . "Header brand, slide-out menu"
     · Light theme (§E) ................... "Light theme"
     · Settings modal (§E) ................ "Settings modal"

   DESIGN SYSTEM: "Neumorphism" (Soft UI). Every surface — page, key, card,
   panel, modal — is the SAME colour (--surface); depth comes only from the
   two-tone shadow tokens (--extruded* raised, --inset* carved). The system
   draws NO borders: a shadow defines every edge, and former hairlines are
   carved grooves (--groove). Accent (violet) is the sole interactive colour
   and stays sparing: the '=' key and the Install pill are the only accent
   FILLS; elsewhere accent is ink on large bold glyphs, focus rings and the
   caret.

   Conventions: colours come from the :root tokens so the light theme
   recolours everything from one block; portrait = circular buttons,
   landscape = stadium pills (ratio only relaxed in the landscape @media).
============================================================ */

:root {
  /* === Design system: Neumorphism — DARK theme (the app's default) ======
     Values are the design system's tokens verbatim. The app's original
     variable NAMES are kept as aliases below, so all 26 JS modules and every
     existing selector keep working unchanged. */

  /* colour */
  --surface: #2b2f38;          /* the one 'cool clay' surface — everything */
  --ink: #e7eaef;              /* 11.1:1 on surface (AAA) */
  --ink-muted: #99a1ac;        /* 5.1:1 on surface — safe for body text in Dark */
  --accent: #8f88ff;           /* the sole interactive colour */
  --accent-light: #a79fff;
  --accent-secondary: #4fd1ca; /* success / positive */
  --on-accent: #14121f;        /* label ON an accent fill (never literal white) */
  --danger: #f08a86;
  --on-danger: #14121f;
  --placeholder: #5b6472;

  /* shadow halves — Dark composes from a faint glow + near-black */
  --shadow-light: rgba(255, 255, 255, 0.06);
  --shadow-dark: rgba(0, 0, 0, 0.6);

  /* depth (the physics) */
  --extruded:       9px 9px 16px rgba(0,0,0,0.6),   -9px -9px 16px rgba(255,255,255,0.06);
  --extruded-hover: 12px 12px 20px rgba(0,0,0,0.7), -12px -12px 20px rgba(255,255,255,0.08);
  --extruded-small: 5px 5px 10px rgba(0,0,0,0.55),  -5px -5px 10px rgba(255,255,255,0.05);
  --inset:       inset 6px 6px 10px rgba(0,0,0,0.6),   inset -6px -6px 10px rgba(255,255,255,0.06);
  --inset-deep:  inset 10px 10px 20px rgba(0,0,0,0.7), inset -10px -10px 20px rgba(255,255,255,0.08);
  --inset-small: inset 3px 3px 6px rgba(0,0,0,0.55),  inset -3px -3px 6px rgba(255,255,255,0.05);
  /* hairline replacement: a carved groove, since the system draws no borders */
  --groove: inset 0 2px 3px rgba(0,0,0,0.5), inset 0 -1px 2px rgba(255,255,255,0.05);
  /* Seams: what the old 1px hairlines became. Pure shadow, inset, so they add
     ZERO layout height — a groove cut into the clay, not a drawn border. */
  --seam-t: inset 0 1px 0 var(--shadow-dark), inset 0 2px 0 var(--shadow-light);
  --seam-b: inset 0 -2px 0 var(--shadow-dark), inset 0 -1px 0 var(--shadow-light);
  --seam-r: inset -2px 0 0 var(--shadow-dark), inset -1px 0 0 var(--shadow-light);
  /* modal/menu backdrop: dimmed in the palette's own colour, never flat black */
  --scrim: rgba(20, 18, 31, 0.66);

  /* radius */
  --radius-sm: 12px;
  --radius-md: 16px;
  --radius-lg: 32px;
  --radius-full: 9999px;

  /* spacing scale */
  --space-1: 4px;   --space-2: 8px;   --space-3: 12px;  --space-4: 16px;
  --space-6: 24px;  --space-8: 32px;  --space-12: 48px; --space-16: 64px;
  --space-20: 80px; --space-32: 128px;

  /* --- app-name aliases (keep every existing selector working).
     --btn-bg / --btn-bg-alt are gone: under this system a key is not a
     different colour (everything is --surface) and hairlines became seams. */
  --bg-color: var(--surface);
  --text-primary: var(--ink);
  --text-secondary: var(--ink-muted);
  --op-color: var(--accent);
  --danger-color: var(--danger);

  /* Type: the system's families. No webfont is loaded — the PWA must boot with
     zero network — so these resolve to the platform UI face today, and pick up
     the real faces automatically if the woff2 files are ever self-hosted. */
  --font-display: "Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, system-ui, sans-serif;
  --font-family: "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, system-ui, sans-serif;
  /* ORDER MATTERS (v0.13.2): system-ui sits LAST. Ahead of Roboto it resolved to
     Samsung's own face on the A55, whose digits overhang their declared metrics
     and were clipped by the display's scroll boxes. This is the pre-v0.13 stack. */

  /* App-level status colour. The system has no 'warning' hue (only
     accent-secondary/success and danger/error), and "rates are stale" is a
     caution, not an error — so this one stays outside the palette. It is
     always paired with the word "(offline)", never colour alone. */
  --warn-color: #e0a03a;
}

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

html { height: 100%; }

body {
  background-color: var(--bg-color);
  color: var(--text-primary);
  font-family: var(--font-family);
  /* --app-height is set in JS from the real visible viewport (fixes
     bottom-row cutoff behind Brave's bottom bar). Fallbacks first. */
  height: 100vh;
  height: 100svh;
  height: var(--app-height, 100svh);
  overflow: hidden;
}

#app {
  display: flex;
  flex-direction: column;
  height: 100%;
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* --- Header & Navigation --- */
/* Nav: the system's sticky extruded bar. Kept full-bleed (no radius) — a
   rounded bar butted against the viewport edge reads as broken, and this is a
   fixed app shell rather than a page that scrolls under it. */
.top-nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 20px;
  flex: 0 0 auto;
  background: var(--surface);
  box-shadow: var(--extruded);
  position: relative;
  z-index: 10;
}

.icon-btn {
  background: none;
  border: none;
  color: var(--text-primary);
  font-size: 1.2rem;
  cursor: pointer;
  padding: 8px;
}

/* Install pill (Stage 1). Hidden until pwa.js reveals it on
   `beforeinstallprompt`. Reuses the equals-key colour treatment so it reads
   as the primary action and recolours with the theme. */
.nav-right { display: flex; align-items: center; gap: 8px; }

/* The system draws no borders — the old outlined pill becomes the second (and
   last) accent fill in the app, and only while the app is actually installable. */
.install-pill {
  background: var(--accent);
  color: var(--on-accent);
  border: none;
  border-radius: var(--radius-md);
  padding: 8px 16px;
  font-family: var(--font-family);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  box-shadow: var(--extruded-small);
}
.install-pill:active {
  box-shadow: inset 3px 3px 6px rgba(0,0,0,0.25), inset -3px -3px 6px rgba(255,255,255,0.15);
  transform: translateY(0.5px);
}
.install-pill:focus-visible { outline: 2px solid var(--op-color); outline-offset: 2px; }

#converter-nav {
  display: flex;
  gap: 16px;
  padding: 0 24px 16px;
  overflow-x: auto;
  white-space: nowrap;
  flex: 0 0 auto;
  scrollbar-width: none;                 /* Firefox */
}
#converter-nav::-webkit-scrollbar { display: none; }   /* Chromium / Brave */

.cat-btn {
  background: none;
  border: none;
  color: var(--text-secondary);
  font-size: 1rem;
  cursor: pointer;
  padding: 8px 12px;
  border-radius: 20px;
  flex: 0 0 auto;
}

/* Active category = carved in, not filled. The inset IS the state; weight
   backs it up so it never depends on shadow alone. */
.cat-btn.active {
  box-shadow: var(--inset-small);
  color: var(--ink);
  font-weight: 600;
}

/* --- Layout Architecture --- */
#main-container {
  display: flex;
  flex: 1;
  overflow: hidden;
  flex-direction: column; /* Portrait default */
  min-height: 0;
}

#left-pane {
  display: none; /* Hidden in portrait standard mode */
  flex-direction: column;
  padding: 0 24px;
  overflow: hidden;
  min-height: 0;
}

#right-pane {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 0 24px 24px;
  justify-content: flex-end;
  min-height: 0;
}

/* --- Displays --- */
#calc-display-wrap {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: flex-end;
  flex: 1;
  min-height: 0;
  margin-bottom: 24px;
  overflow: hidden;
}

#calc-input {
  font-size: 2.5rem;
  font-weight: 300;
  text-align: right;
  line-height: 1.25;
  /* A scroll box clips at its padding edge: this padding gives digit ink room
     whatever the font's declared metrics (see --font-family note). */
  padding: 0.06em 0 0.16em;
  width: 100%;
  white-space: nowrap;      /* single line; JS shrinks the font to fit (§F1) */
  overflow-x: auto;         /* past the shrink floor, scroll horizontally */
  overflow-y: hidden;
  scrollbar-width: none;    /* hide the scrollbar (Firefox) */
}
#calc-input::-webkit-scrollbar { display: none; } /* hide the scrollbar (WebKit) */

/* Never let the flex column squash a display line: a squashed scroll box
   clips digit bottoms. If the panel is too short, the older line is what
   gets hidden (overflow at the top of the flex-end column), not both. */
#calc-input, #calc-expression { flex-shrink: 0; }

#calc-expression {
  font-size: 1.5rem;
  color: var(--text-secondary);
  min-height: 2rem;
  margin-top: 8px;
}

/* After '=': expression dims on top, result grows below. */
#calc-display-wrap.evaluated #calc-input {
  font-size: 1.4rem;
  color: var(--text-secondary);
}
#calc-display-wrap.evaluated #calc-expression {
  font-size: 2.5rem;
  line-height: 1.25;
  padding: 0.06em 0 0.16em;   /* same metric-proofing as #calc-input */
  color: var(--text-primary);
  text-align: right;
  white-space: nowrap;
  overflow-x: auto;
  max-width: 100%;
  scrollbar-width: none;
}
#calc-display-wrap.evaluated #calc-expression::-webkit-scrollbar { display: none; }

.op-color { color: var(--op-color); }
.text-red { color: var(--danger-color) !important; }

.cursor {
  /* The caret is a DRAWN 2px bar. It used to be the '|' glyph in a 2px box;
     the glyph's ink spilled past the box and counted as horizontal overflow,
     so the Basic formula's fit-to-width loop (§F1) always fell to its 1.5rem
     floor. overflow:hidden + transparent glyph = no phantom overflow. The '|'
     stays in the DOM only to give the bar a line box. */
  display: inline-block;
  width: 2px;
  height: 0.95em;
  overflow: hidden;
  vertical-align: -0.1em;
  color: transparent;
  background: var(--op-color);
  border-radius: 1px;
  animation: blink 1s step-end infinite;
}
@keyframes blink { 50% { opacity: 0; } }

/* --- Buttons & Grids --- */
.grid-standard {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
  width: 100%;
  max-width: 400px;
  margin: 0 auto;
  flex: 0 0 auto;
}

/* Portrait scientific: 6-column grid (2 sci + 4 numpad, then a 6-wide sci row)
   sized to sit inside the basic numpad's footprint. aspect-ratio:1 keeps every
   key circular; the whole cluster is smaller than the standard keypad so it
   always fits. */
.grid-scientific {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 8px;
  width: 100%;
  max-width: 400px;
  margin: 0 auto;
  flex: 0 0 auto;
}
.grid-scientific .btn { font-size: 1.1rem; }
.grid-scientific .sci-btn { font-size: 0.82rem; }

/* Numeral base keypad: 6-col × 3-row (like the portrait sci grid's width). */
.grid-base {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 8px;
  width: 100%;
  max-width: 400px;
  margin: 0 auto;
  flex: 0 0 auto;
}
.grid-base .btn { font-size: 1rem; }
/* Bitwise op keys carry short words (NOT / RoL); shrink so they fit the key
   in both the portrait circle and the landscape pill. Scoped to beat the
   landscape .btn font-size. */
.grid-base .fn-key { font-size: 0.8rem; }
/* Small labels keep --ink, not accent: accent would fall to 3.4:1 in Light at
   this size. (Beats .op-btn on specificity.) */
.grid-base .fn-key { color: var(--ink); font-weight: 600; }
/* Armed binary op (AND/OR/XOR waiting for operand B): green ring so it's clear
   which op is pending. */
/* Armed binary op: pressed IN and ringed, so the pending operand reads at a
   glance. Ink stays --ink (these labels are small; accent ink would drop
   under the contrast floor in Light). */
.grid-base .btn.armed {
  box-shadow: var(--inset-small), 0 0 0 2px var(--surface), 0 0 0 4px var(--accent);
}
/* Inert grid filler — occupies a cell, no button chrome, not clickable. */
/* .btn.btn-spacer (not .btn-spacer): the .btn rule sits later in the file and
   would otherwise re-apply the key shadow to an empty cell. */
.btn.btn-spacer { background: none; box-shadow: none; pointer-events: none; }

/* Scientific-landscape unified grid (sci + numpad in one board). Needs its own
   display:grid base — column count is set by the landscape overrides. */
.grid-scilandscape {
  display: grid;
  gap: 12px;
  width: 100%;
  margin: 0 auto;
  flex: 0 0 auto;
}

/* Landscape 5-col numpad (Basic in landscape). Column count set below. */
.grid-wide {
  display: grid;
  gap: 12px;
  width: 100%;
  margin: 0 auto;
  flex: 0 0 auto;
}

/* A key is molded FROM the surface, never a differently-coloured chip: same
   --surface, raised by --extruded-small. (The small token, not --extruded:
   at key size the 16px blur overpowers, which the system calls muddy.) */
.btn {
  background-color: var(--surface);
  color: var(--ink);
  border: none;
  border-radius: 50%;
  aspect-ratio: 1; /* NO SQUISH */
  font-size: 1.5rem;
  cursor: pointer;
  display: flex;
  justify-content: center;
  align-items: center;
  box-shadow: var(--extruded-small);
  transition: box-shadow 0.18s ease-out, transform 0.18s ease-out;
  width: 100%;
}

/* Press = the key sinks into the surface. */
.btn:active {
  box-shadow: var(--inset-small);
  transform: translateY(0.5px);
}
/* Hover only where a real pointer exists — on the A55 a hover state sticks
   after a tap and the key would stay lifted. */
@media (hover: hover) {
  .btn:hover { box-shadow: var(--extruded-hover); transform: translateY(-1px); }
  .btn:active:hover { box-shadow: var(--inset-small); transform: translateY(0.5px); }
}

/* Operators carry the accent as INK (all fills are gone). At 24px/700 they are
   'large text', so accent clears 3:1 in both themes. */
.op-btn { color: var(--accent); }

/* '=' is the one primary action on the board — the only accent FILL. */
.equals-btn { background-color: var(--accent); color: var(--on-accent); }
.equals-btn:active {
  box-shadow: inset 3px 3px 6px rgba(0,0,0,0.25), inset -3px -3px 6px rgba(255,255,255,0.15);
}
@media (hover: hover) {
  .equals-btn:active:hover {
    box-shadow: inset 3px 3px 6px rgba(0,0,0,0.25), inset -3px -3px 6px rgba(255,255,255,0.15);
  }
}

/* Bold numpad symbols (leaves the scientific labels at normal weight). */
.num-btn, .op-btn, .action-btn, .equals-btn { font-weight: 700; }

.btn.disabled {
  opacity: 0.3;
  pointer-events: none;
}

/* Scientific keys use the operator grey in both the portrait merged grid and
   the landscape unified grid. Un-scoped so .sci-btn keys match the operator keys. */
/* Scientific keys: same molded surface. Their labels are small (0.82rem), so
   they keep --ink rather than accent, which would fail contrast at that size. */
.sci-btn { color: var(--ink); }

/* --- Converter View --- */
#converter-view {
  display: flex;
  flex-direction: column;
  justify-content: center;
  flex: 1;
  min-height: 0;
  padding: 12px 0;
}

.converter-field {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

select {
  border: none;
  color: var(--text-primary);
  font-size: 1.2rem;
  margin-bottom: 8px;
  outline: none;
  appearance: none;
  /* appearance:none strips the native arrow — draw our own chevron so users
     can tell the unit is a dropdown. Grey reads on both themes. */
  padding-right: 22px;
  background-color: transparent;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238e8e93' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat: no-repeat;
  background-position: right 0 center;
  background-size: 12px;
}

.unit-val {
  font-size: 1.5rem;
  color: var(--text-secondary);
  align-self: flex-end;
  max-width: 100%;
  overflow-x: auto;
  white-space: nowrap;
  scrollbar-width: none;
  transition: color 0.2s, font-size 0.2s;
}
.unit-val::-webkit-scrollbar { display: none; }

.unit-val.active-val {
  color: var(--text-primary);
  font-size: 1.9rem;
}

/* Former hairline → a groove carved across the pane. */
.divider {
  border: 0;
  height: 3px;
  background: none;
  border-radius: var(--radius-full);
  box-shadow: var(--groove);
  margin: 16px 0;
}

/* Currency-only footer: ECB rate date, amber once stale (>7d) / offline. */
#fx-status {
  align-self: flex-end;
  margin-top: 10px;
  font-size: 0.8rem;
  color: var(--text-secondary);
}
#fx-status.fx-stale { color: var(--warn-color); }

/* --- Percentage View --- */
#percent-view {
  display: flex;
  flex-direction: column;
  justify-content: center;
  flex: 1;
  min-height: 0;
  padding: 12px 0;
  gap: 10px;
}

#percent-ops {
  display: flex;
  gap: 8px;
  flex: 0 0 auto;
  overflow-x: auto;
  white-space: nowrap;
  scrollbar-width: none;
}
#percent-ops::-webkit-scrollbar { display: none; }

.pct-op {
  background: none;
  border: none;
  color: var(--text-secondary);
  font-size: 0.95rem;
  cursor: pointer;
  padding: 6px 14px;
  border-radius: 20px;
  flex: 0 0 auto;
}
.pct-op.active {
  box-shadow: var(--inset-small);
  color: var(--ink);
  font-weight: 600;
}

/* One-line rows: label left, value right — compact (like the base view), so
   ops + two inputs + divider + result all fit above the numpad in portrait. */
.pct-field, .pct-result {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.pct-label { color: var(--text-secondary); font-size: 0.95rem; flex: 0 0 auto; }
#percent-view .unit-val { font-size: 1.5rem; }
#percent-view .unit-val.active-val { font-size: 1.7rem; }
.pct-result-val {
  font-size: 1.6rem;
  color: var(--op-color);
  max-width: 100%;
  overflow-x: auto;
  white-space: nowrap;
  scrollbar-width: none;
}
.pct-result-val::-webkit-scrollbar { display: none; }

/* --- Base converter view (DEC / HEX / OCT / BIN) --- */
#base-view {
  display: flex;
  flex-direction: column;
  justify-content: center;
  flex: 1;
  min-height: 0;
  padding: 12px 0;
  gap: 10px;
}
.base-field { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.base-label { color: var(--text-secondary); font-size: 0.95rem; flex: 0 0 auto; letter-spacing: 0.05em; }
/* Base values reuse .unit-val / .active-val (right-aligned, scroll, highlight). */

/* Pending "A op" strip for binary ops. Right-aligned to sit over the value
   column; collapses to nothing (no height/margin) when no op is armed. */
.base-expr {
  color: var(--op-color);
  font-size: 0.95rem;
  text-align: right;
  min-height: 0;
  letter-spacing: 0.03em;
}
.base-expr:empty { display: none; }

/* Word-size selector: reuses .seg (green-border active), sits above the rows.
   flex:0 0 auto so it never grows; small buttons keep the four labels on one line. */
.wordsize-seg { flex: 0 0 auto; }
.wordsize-seg button { padding: 8px 2px; font-size: 0.82rem; }

/* --- Form view (form-style modes: Loan, Date & time, BMI) --- */
/* Full-width scrollable form; the numpad panes are hidden in these modes. */
#form-view,
#datetime-view,
#bmi-view,
#help-view {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 4px 24px 24px;
  gap: 14px;
}
#app[data-mode="loan"] #right-pane,
#app[data-mode="datetime"] #right-pane,
#app[data-mode="bmi"] #right-pane,
#app[data-mode="help"] #right-pane { display: none; }

/* --- Help view (user guide): reuses form <details> accordions (.form-section)
   for each box; these style the box contents (description / how-to / glossary). */
#help-view .form-section-body { gap: 12px; }
.help-desc { color: var(--text-primary); font-size: 0.95rem; line-height: 1.45; }
.help-block { display: flex; flex-direction: column; gap: 6px; }
.help-h {
  color: var(--text-secondary);
  font-size: 0.72rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.help-steps { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 6px; }
.help-steps li { font-size: 0.92rem; line-height: 1.45; }
.help-gloss { margin: 0; }
.help-gloss dt { font-size: 0.92rem; font-weight: 500; color: var(--text-primary); }
.help-gloss dd { margin: 0 0 8px; font-size: 0.88rem; line-height: 1.4; color: var(--text-secondary); }
.help-gloss dd:last-child { margin-bottom: 0; }
#help-view b { font-weight: 600; }

/* Date & time: offset inputs stack in portrait (narrow), go 2-up in landscape
   (see the landscape query). minmax(0,1fr) keeps the 2-up tracks from overflowing. */
.dt-grid { display: grid; grid-template-columns: 1fr; gap: 14px; }
.dt-panel { display: flex; flex-direction: column; gap: 14px; }

/* BMI: two short fields (feet/inches) always sit 2-up; panels stack their rows. */
.form-row-2 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14px; }
.bmi-panel { display: flex; flex-direction: column; gap: 14px; }

.form-head { color: var(--text-secondary); font-size: 1rem; }

.form-field { display: flex; flex-direction: column; gap: 6px; }
.form-label { color: var(--text-secondary); font-size: 0.95rem; }
/* Input = a well carved into the surface; no border at any state. */
.form-input-row {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--surface);
  border-radius: var(--radius-md);
  padding: 12px 16px;
  box-shadow: var(--inset);
  transition: box-shadow 0.3s ease-out;
}
/* Focus carves deeper and adds the accent ring — the one hard edge the system
   allows, because it is a focus indicator, not a border. */
.form-input-row:focus-within {
  box-shadow: var(--inset-deep), 0 0 0 2px var(--surface), 0 0 0 4px var(--accent);
}
.form-affix { color: var(--text-secondary); font-size: 1.1rem; flex: 0 0 auto; }
.form-affix-end { margin-left: auto; }
.form-input {
  flex: 1;
  min-width: 0;
  background: transparent;
  border: none;
  outline: none;
  color: var(--text-primary);
  font-size: 1.3rem;
  caret-color: var(--op-color);
}
.form-input::placeholder { color: var(--placeholder); opacity: 1; }

.form-section { box-shadow: var(--seam-t); padding-top: 8px; }
.form-section-title {
  color: var(--text-primary);
  font-size: 1rem;
  cursor: pointer;
  list-style: none;
  padding: 6px 0;
}
.form-section-title::-webkit-details-marker { display: none; }
.form-section-title::before { content: '\25B8\00a0'; color: var(--text-secondary); }
.form-section[open] .form-section-title::before { content: '\25BE\00a0'; }
.form-section-body { display: flex; flex-direction: column; gap: 14px; padding-top: 12px; }
.form-note { color: var(--text-secondary); font-size: 0.8rem; line-height: 1.4; }

.form-results {
  display: flex;
  flex-direction: column;
  gap: 10px;
  box-shadow: var(--seam-t);
  padding-top: 16px;
}
.form-result { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.form-result-label { color: var(--text-secondary); font-size: 0.95rem; }
.form-result-val { color: var(--op-color); font-size: 1.4rem; text-align: right; }

.form-link {
  background: none;
  border: none;
  color: var(--text-secondary);
  font-size: 0.85rem;
  text-decoration: underline;
  cursor: pointer;
  padding: 4px 0;
  align-self: flex-start;
}

/* Date input: strip the native look, tint the picker indicator for dark theme. */
.form-input-date { color-scheme: dark; }
.form-input-date::-webkit-calendar-picker-indicator { filter: invert(0.7); cursor: pointer; }

/* Repayment schedule: a contained, scrollable table with a sticky header. */
.schedule-wrap { max-height: 300px; overflow-y: auto; margin-top: 10px; }
table.schedule { width: 100%; border-collapse: collapse; font-size: 0.8rem; }
table.schedule thead th {
  position: sticky;
  top: 0;
  background: var(--surface);
  color: var(--text-secondary);
  font-weight: 500;
  text-align: right;
  padding: 6px 8px;
}
table.schedule thead th:first-child { text-align: left; }
/* Dense data rows: a single faint seam. A full two-tone groove on every row of
   a 360-row schedule would read as noise. */
table.schedule td { text-align: right; padding: 5px 8px; color: var(--text-primary); box-shadow: inset 0 1px 0 var(--shadow-dark); }
table.schedule td:first-child { text-align: left; color: var(--text-secondary); }

.modal-text { color: var(--text-secondary); font-size: 1rem; line-height: 1.5; }

/* --- History slide-in panel --- */
.history-panel {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: 82%;
  max-width: 340px;
  background: var(--surface);
  display: flex;
  flex-direction: column;
  padding: 24px 0 calc(16px + env(safe-area-inset-bottom, 0px));
  transform: translateX(100%);
  visibility: hidden;
  transition: transform 0.24s ease, visibility 0.24s ease;
  z-index: 50;
  box-shadow: -2px 0 24px rgba(0, 0, 0, 0.5);
}
.history-panel.open { transform: translateX(0); visibility: visible; }

.history-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 4px 20px 16px;
  box-shadow: var(--seam-b);
}
.history-head .title { font-size: 1.2rem; font-weight: 500; }
.history-head .modal-close { position: static; }

#history-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 20px;
}

.history-item {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  padding: 10px 4px;
  box-shadow: var(--seam-b);
  width: 100%;
}
.history-item button {
  background: none;
  border: none;
  cursor: pointer;
  width: 100%;
  text-align: right;
  padding: 3px 0;
  font-family: var(--font-family);
}
.history-item button:active { color: var(--accent); }

.history-item .h-expr { color: var(--text-secondary); font-size: 1.1rem; }
.history-item .h-res { color: var(--text-primary); font-size: 1.6rem; }
.history-empty { color: var(--text-secondary); padding: 16px 4px; }

/* Destructive, so it takes --danger as ink (4.8:1 Light / 5.5:1 Dark) rather
   than a third accent fill. */
#clear-history {
  align-self: center;
  margin: 12px 20px 0;
  background: var(--surface);
  color: var(--danger);
  box-shadow: var(--extruded-small);
  border: none;
  border-radius: 20px;
  padding: 10px 20px;
  font-size: 0.95rem;
  cursor: pointer;
  flex: 0 0 auto;
}

/* --- Landscape Overrides --- */
@media (orientation: landscape) {
  /* Landscape is short and wide. Circular keys (aspect-ratio:1) are forced tiny
     because their width is tied to the cramped height. Like the native Samsung
     calculator, we switch to stadium PILLS in landscape: width fills the column,
     height is fit to the viewport so every row shows. PORTRAIT KEEPS CIRCLES —
     aspect-ratio:1 is only relaxed inside this landscape query.
     Height budgets via --btn-h:
       default   : Basic & Sci — 4-row numpad under a full-width display.
       converter : 4-row numpad, minus the category tab row above it. */
  #main-container {
    flex-direction: row;
    padding-bottom: 12px;
    --btn-h: max(24px, min(54px, calc((var(--app-height, 100svh) - 178px) / 4)));
  }

  /* Date & time offset inputs: 2-up in landscape (room for it). */
  #app[data-mode="datetime"] .dt-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  #app[data-mode="converter"] #main-container {
    --btn-h: max(26px, min(52px, calc((var(--app-height, 100svh) - 172px) / 4)));
  }

  /* min-width:0 lets a very long display scroll INSIDE its pane instead of
     forcing the pane wider and squeezing the neighbouring pane. */
  #left-pane {
    display: flex;
    flex: 1;
    min-width: 0;
    justify-content: center;
    box-shadow: var(--seam-r);
  }

  #right-pane {
    flex: 1;
    min-width: 0;
    padding: 0 24px 12px;
    justify-content: center;
  }

  /* Trim the display so it eats less of the short landscape column. */
  #calc-display-wrap { flex: 0 0 auto; min-width: 0; margin-bottom: 8px; }
  #calc-input { font-size: 1.5rem; }
  #calc-expression { font-size: 1.2rem; min-height: 1.2rem; margin-top: 4px; }
  #calc-display-wrap.evaluated #calc-input { font-size: 1rem; }
  #calc-display-wrap.evaluated #calc-expression { font-size: 1.5rem; }

  /* Pills fill each column; the grid stretches across its pane. */
  .grid-standard     { grid-template-columns: repeat(4, 1fr); }
  .grid-wide         { grid-template-columns: repeat(5, 1fr); }
  .grid-scilandscape { grid-template-columns: repeat(9, 1fr); }
  .grid-standard,
  .grid-wide,
  .grid-scilandscape,
  .grid-base {
    justify-content: stretch;
    gap: 10px;
    width: 100%;
    max-width: none;
  }

  .btn {
    aspect-ratio: auto;          /* landscape only: stadium pill, not a circle */
    width: 100%;
    height: var(--btn-h);
    border-radius: 999px;
    margin: 0;
    font-size: 1.15rem;
  }
  /* sci keys inherit the pill shape from .btn; just smaller text inside the
     unified landscape grid. */
  .sci-btn { font-size: 0.9rem; }

  /* Basic: no scientific keys or converter fields, so hide the empty left pane and give
     the whole width to a display-on-top / numpad-below stack (Samsung's basic
     landscape). Cap the numpad width so the pills don't stretch too wide. */
  #app[data-mode="standard"] #left-pane { display: none; }
  #app[data-mode="standard"] #right-pane {
    flex-direction: column;
    justify-content: center;
    align-items: stretch;
  }
  #app[data-mode="standard"] #keypad {
    max-width: 560px;
    margin: 0 auto;
  }
  #app[data-mode="standard"] #calc-display-wrap { align-items: flex-end; }

  /* Scientific: Miguel's structure — a full-width display on top, then ONE grid
     (sci + numpad, 9 cols × 4 rows) below. No dual pane, so rows align by
     construction. Same shape as Basic's landscape stack, wider grid. */
  #app[data-mode="sci"] #left-pane { display: none; }
  #app[data-mode="sci"] #right-pane {
    flex: 1;
    flex-direction: column;
    justify-content: center;
    align-items: stretch;
  }
  #app[data-mode="sci"] #calc-display-wrap { align-items: flex-end; }

  /* Percentage (landscape): the pane is only ~300px tall, so shrink the whole
     block (ops + two inputs + divider + result) hard and centre it so the
     result never clips off the bottom. */
  /* Percentage (landscape): compact rows, centred. */
  #app[data-mode="percent"] #percent-view { justify-content: center; padding: 0; gap: 6px; }
  #app[data-mode="percent"] .pct-op { font-size: 0.85rem; padding: 4px 10px; }
  #app[data-mode="percent"] .pct-label { font-size: 0.85rem; }
  #app[data-mode="percent"] #percent-view .unit-val { font-size: 1.2rem; }
  #app[data-mode="percent"] #percent-view .unit-val.active-val { font-size: 1.35rem; }
  #app[data-mode="percent"] .divider { margin: 2px 0; }
  #app[data-mode="percent"] .pct-result-val { font-size: 1.25rem; }

  /* Numeral bases (landscape): keep all four rows compact so none clip. */
  #app[data-mode="base"] #base-view { justify-content: center; padding: 0; gap: 6px; }
  /* Programmer keypad is now 5 rows (unary + binary + 3 digit rows), so budget
     the pill height for 5, not the default 4, or the bottom row clips. */
  #app[data-mode="base"] #main-container { --btn-h: max(20px, min(46px, calc((var(--app-height, 100svh) - 168px) / 5))); }
  #app[data-mode="base"] .base-label { font-size: 0.8rem; }
  #app[data-mode="base"] #base-view .unit-val { font-size: 1rem; }
  #app[data-mode="base"] #base-view .unit-val.active-val { font-size: 1.2rem; }
  /* Short landscape column: keep the word-size selector tight so all four base
     rows still clear the bottom. */
  #app[data-mode="base"] .wordsize-seg button { padding: 5px 2px; font-size: 0.72rem; }

  /* Converter (landscape): the left pane is only ~300px tall. Shrink the stack
     so a long selector/value never clips top or bottom. */
  #app[data-mode="converter"] #converter-view { justify-content: center; padding: 0; }
  #app[data-mode="converter"] #converter-view select { font-size: 1rem; margin-bottom: 4px; }
  #app[data-mode="converter"] #converter-view .unit-val { font-size: 1.2rem; }
  #app[data-mode="converter"] #converter-view .unit-val.active-val { font-size: 1.45rem; }
  #app[data-mode="converter"] .divider { margin: 8px 0; }
  /* Currency's staleness line lives LAST in source order (bottom of the pane),
     where it fell off the short landscape column. Reorder the flex column so it
     rides up directly under the FROM field. It's display:none for every other
     category, so order is a no-op there. Portrait keeps source order. */
  #app[data-mode="converter"] #field-from { order: 0; }
  #app[data-mode="converter"] #fx-status  { order: 1; margin-top: 4px; }
  #app[data-mode="converter"] .divider    { order: 2; }
  #app[data-mode="converter"] #field-to   { order: 3; }
}

.hidden { display: none !important; }

/* ============================================================
   Portrait layout fixes (mode-scoped via #app[data-mode]).
============================================================ */
@media (orientation: portrait) {
  /* Converter: numpad keeps its natural height; the value fields take the rest.
     Anchor the fields to the TOP of the pane (with a little clearance under the
     tab nav) instead of centring — centring let the taller active value push
     the first unit label up under the tabs and clip. A trimmed keypad gap and
     tighter divider give both fields room to sit without clipping either edge. */
  #app[data-mode="converter"] #left-pane { flex: 1 1 0; min-height: 0; }
  #app[data-mode="converter"] #right-pane { flex: 0 0 auto; }
  #app[data-mode="converter"] #converter-view {
    justify-content: flex-start;
    padding-top: 8px;
  }
  #app[data-mode="converter"] .divider { margin: 12px 0; }
  #app[data-mode="converter"] #keypad { margin-top: 12px; }

  /* Percentage (portrait): centre the compact block (ops + two inputs +
     divider + result) in the pane; numpad keeps its natural height below. */
  #app[data-mode="percent"] #left-pane { flex: 1 1 0; min-height: 0; }
  #app[data-mode="percent"] #right-pane { flex: 0 0 auto; }
  #app[data-mode="percent"] #percent-view {
    justify-content: center;
    padding-top: 8px;
  }
  #app[data-mode="percent"] .divider { margin: 4px 0; }
  #app[data-mode="percent"] #keypad { margin-top: 12px; }

  /* Numeral bases (portrait): base rows on top, 6-col keypad below. */
  #app[data-mode="base"] #left-pane { flex: 1 1 0; min-height: 0; }
  #app[data-mode="base"] #right-pane { flex: 0 0 auto; }
  #app[data-mode="base"] #base-view { justify-content: center; padding-top: 8px; }
  #app[data-mode="base"] #keypad { margin-top: 12px; }

  /* The calculator display reads as a well carved into the surface — the
     system's Input language applied to the app's main readout.
     PORTRAIT ONLY: in landscape the --btn-h budget is tuned to the pixel, and
     the well's padding would push the bottom key row off-screen. */
  #app[data-mode="standard"] #calc-display-wrap,
  #app[data-mode="sci"] #calc-display-wrap {
    box-shadow: var(--inset);
    border-radius: var(--radius-lg);
    padding: 16px 20px;
  }

  /* Scientific (portrait): the merged 6-col grid IS the numpad footprint, so
     the display simply absorbs the slack above it. No pane juggling needed. */
  #app[data-mode="sci"] #calc-display-wrap { flex: 1 1 0; min-height: 0; margin-bottom: 8px; }
  #app[data-mode="sci"] #right-pane { flex: 1 1 0; min-height: 0; }

  /* --- Canvas separation (v0.13.1) -------------------------------------
     Every screen: header → gap → carved panel → keys. Without the gap and the
     panel, content sat directly under the header's shadow with no edge, and
     the screen read as one undivided slab of clay. */
  #main-container { padding-top: 16px; }
  #converter-nav { padding-top: 12px; padding-bottom: 4px; }

  /* The field views become the same inset panel as Basic's display. Declared
     last in this block so they win over the per-mode padding rules above. */
  #app[data-mode="converter"] #converter-view,
  #app[data-mode="percent"] #percent-view,
  #app[data-mode="base"] #base-view {
    box-shadow: var(--inset);
    border-radius: var(--radius-lg);
    padding: 16px 20px;
  }

  /* Pay for the panels with smaller keys, only in these three modes. Width
     caps drive the key diameter (portrait keys are aspect-ratio 1):
       4-col converter/percent: 82px → 68px   ·   6-col programmer: 54px → 47px */
  #app[data-mode="converter"] #keypad,
  #app[data-mode="percent"] #keypad { max-width: 308px; }
  #app[data-mode="converter"] .btn,
  #app[data-mode="percent"] .btn { font-size: 1.35rem; }
  #app[data-mode="base"] #keypad { max-width: 322px; }
  /* Basic: 82px → 74px keys, so the display panel keeps enough height for a
     full-size formula + preview / expression + result (v0.13.2). */
  #app[data-mode="standard"] #keypad { max-width: 332px; }

  /* BIN inside the panel: wrap at the nibble spaces instead of scrolling a
     half-cut digit off the edge (DWord = 2 lines, QWord = 3–4). Smaller type
     so 32 bits usually fits in two lines. Right-aligned like the other rows. */
  #app[data-mode="base"] #base-val-bin {
    white-space: normal;
    overflow-x: visible;
    text-align: right;
    font-size: 1.05rem;
    line-height: 1.35;
  }
  #app[data-mode="base"] #base-view #base-val-bin.active-val { font-size: 1.2rem; }

  /* DEC / HEX / OCT: same idea. A 64-bit value no longer scrolls a half-cut
     digit off the panel edge; smaller type keeps HEX/OCT on one line, and a
     long DEC wraps to a second line instead of clipping. */
  #app[data-mode="base"] #base-val-dec,
  #app[data-mode="base"] #base-val-hex,
  #app[data-mode="base"] #base-val-oct {
    white-space: normal;
    overflow-wrap: anywhere;
    overflow-x: visible;
    text-align: right;
    font-size: 1.2rem;
    line-height: 1.25;
  }
  /* Specificity must beat the #base-val-* ID rule above, or the active row
     never grows. */
  #app[data-mode="base"] #base-view .unit-val.active-val { font-size: 1.35rem; }
}

/* Short portrait (Brave's toolbars shown, smaller phones): step Basic down
   once more so both display lines still fit inside the panel. */
@media (orientation: portrait) and (max-height: 680px) {
  #app[data-mode="standard"] #keypad { max-width: 296px; }     /* 74 → 65px keys */
  #app[data-mode="standard"] #calc-display-wrap { padding: 12px 18px; }
}

/* ============================================================
   Header brand, slide-out menu (§C) and info modal (§D).
   All colours pulled from the existing tokens so the new chrome
   stays native to the Samsung-dark calculator.
============================================================ */

/* --- Header (§B) --- */
.nav-left { display: flex; align-items: center; gap: 12px; min-width: 0; }
/* Nav actions reuse the key language at small scale: a molded round well with
   a 44px touch target (the system's minimum). */
.nav-left .icon-btn { padding: 4px; }
.icon-btn {
  border-radius: var(--radius-full);
  box-shadow: var(--extruded-small);
  min-width: 44px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: box-shadow 0.18s ease-out, transform 0.18s ease-out;
}
.icon-btn:active { box-shadow: var(--inset-small); transform: translateY(0.5px); }
.icon-btn svg { display: block; }

.brand { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.brand-name { font-size: 1.25rem; font-weight: 500; letter-spacing: -0.01em; }
.brand-name.lg { font-size: 1.9rem; }
.brand-version { font-size: 0.8rem; font-style: italic; color: var(--text-secondary); }

/* --- Shared overlay (backdrop) --- */
.overlay {
  position: fixed;
  inset: 0;
  background: var(--scrim);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s ease, visibility 0.2s ease;
  z-index: 40;
}
.overlay.show { opacity: 1; visibility: visible; }

/* --- Slide-out menu (§C) --- */
.menu-panel {
  position: fixed;
  top: 0; left: 0; bottom: 0;
  width: 78%;
  max-width: 300px;
  background: var(--surface);
  display: flex;
  flex-direction: column;
  padding: 24px 0 calc(16px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-100%);
  visibility: hidden;
  transition: transform 0.24s ease, visibility 0.24s ease;
  z-index: 50;
  box-shadow: var(--extruded);
}
.menu-panel.open { transform: translateX(0); visibility: visible; }

.menu-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 8px 24px 20px;
  box-shadow: var(--seam-b);
}

/* flex:1 + min-height:0 + overflow lets the item list scroll when the (short
   landscape) panel can't fit every item — Settings was falling off the bottom. */
.menu-items { display: flex; flex-direction: column; padding: 8px 0; flex: 1 1 auto; min-height: 0; overflow-y: auto; }

.menu-item {
  background: none;
  border: none;
  border-left: 3px solid transparent;
  color: var(--text-primary);
  font-family: var(--font-family);
  font-size: 1.1rem;
  text-align: left;
  padding: 16px 24px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.menu-item:active { box-shadow: var(--inset-small); }
/* Selected mode = carved in + weighted. (Accent ink is not used here: at
   1.1rem regular it would sit under the contrast floor in Light.) */
.menu-item.active { box-shadow: var(--inset-small); color: var(--ink); font-weight: 600; }
.menu-item.is-soon { color: var(--text-secondary); cursor: default; }
.menu-item.is-soon:active { background: none; }

.menu-item .soon {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
  border: none;
  border-radius: var(--radius-sm);
  padding: 3px 9px;
  box-shadow: var(--extruded-small);
}

/* --- Info modal (§D) --- */
.modal {
  position: fixed;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%) scale(0.96);
  width: calc(100% - 48px);
  max-width: 360px;
  max-height: calc(100% - 48px);
  overflow-y: auto;
  background: var(--surface);
  border-radius: var(--radius-lg);
  padding: 28px 24px 24px;
  box-shadow: var(--extruded-hover);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s ease;
  z-index: 60;
}
.modal.open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translate(-50%, -50%) scale(1);
}

.modal-close {
  position: absolute;
  top: 10px; right: 14px;
  background: none;
  border: none;
  color: var(--ink);
  font-size: 1.7rem;
  line-height: 1;
  cursor: pointer;
  padding: 4px;
  border-radius: var(--radius-full);
  min-width: 44px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.modal-close:active { box-shadow: var(--inset-small); }

.modal-s1 { text-align: center; padding-bottom: 18px; box-shadow: var(--seam-b); }
.modal-s1 .brand { justify-content: center; }
.modal-s1 .brand-version { position: static; }
.modal-by { font-style: italic; color: var(--text-secondary); font-size: 0.9rem; margin-top: 6px; }

.modal-s2 { padding: 18px 0; }
.modal-s2 h3 {
  font-size: 0.8rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-secondary);
  margin-bottom: 12px;
}
.modal-s2 ul { list-style: none; display: flex; flex-direction: column; gap: 10px; }
.modal-s2 li { font-size: 0.98rem; line-height: 1.3; padding-left: 18px; position: relative; }
.modal-s2 li::before {
  content: "";
  position: absolute;
  left: 0; top: 0.5em;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--op-color);
}

/* --- Quality floor: visible focus + reduced motion ---
   The focus ring is the ONE hard edge this system draws, and it is mandatory
   on every interactive element. */
.icon-btn:focus-visible,
.menu-item:focus-visible,
.modal-close:focus-visible,
.btn:focus-visible,
.cat-btn:focus-visible,
.pct-op:focus-visible,
.install-pill:focus-visible,
#clear-history:focus-visible,
.form-section-title:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
/* Round controls keep a round ring. */
.btn:focus-visible, .icon-btn:focus-visible, .modal-close:focus-visible {
  border-radius: var(--radius-full);
}

@media (prefers-reduced-motion: reduce) {
  .overlay, .menu-panel, .modal, .history-panel,
  .btn, .icon-btn, .install-pill, .form-input-row { transition: none; }
  .btn:hover, .btn:active, .icon-btn:active, .install-pill:active { transform: none; }
}

/* ============================================================
   Light theme (§E) — derived from the dark tokens, not a separate
   palette. Because every component reads these variables, the whole
   UI (keypad, menu, modals) recolours from this one block.
============================================================ */
:root[data-theme="light"] {
  /* Light is the design system's primary theme. Only the tokens change —
     every component recolours from this one block, exactly as before. */
  --surface: #e0e5ec;
  --ink: #3d4852;              /* 7.4:1 on surface (AAA) */
  --ink-muted: #6b7280;        /* 3.8:1 — see the contrast note below */
  --accent: #6c63ff;
  --accent-light: #8b84ff;
  --accent-secondary: #38b2ac;
  --on-accent: #ffffff;
  --danger: #b33531;
  --on-danger: #ffffff;
  --placeholder: #a0aec0;

  /* Light composes its shadows from a near-white glow + a cool blue-grey. */
  --shadow-light: rgba(255, 255, 255, 0.55);
  --shadow-dark: rgba(163, 177, 198, 0.65);

  --extruded:       9px 9px 16px rgba(163,177,198,0.6),   -9px -9px 16px rgba(255,255,255,0.5);
  --extruded-hover: 12px 12px 20px rgba(163,177,198,0.7), -12px -12px 20px rgba(255,255,255,0.6);
  --extruded-small: 5px 5px 10px rgba(163,177,198,0.6),   -5px -5px 10px rgba(255,255,255,0.5);
  --inset:       inset 6px 6px 10px rgba(163,177,198,0.6),   inset -6px -6px 10px rgba(255,255,255,0.5);
  --inset-deep:  inset 10px 10px 20px rgba(163,177,198,0.7), inset -10px -10px 20px rgba(255,255,255,0.6);
  --inset-small: inset 3px 3px 6px rgba(163,177,198,0.6),   inset -3px -3px 6px rgba(255,255,255,0.5);
  --groove: inset 0 2px 3px rgba(163,177,198,0.55), inset 0 -1px 2px rgba(255,255,255,0.65);
  --scrim: rgba(61, 72, 82, 0.45);

  /* CONTRAST NOTE: --ink-muted measures 3.8:1 on surface in Light. The design
     system's own README flags this and reserves it for 19px+/bold or
     non-essential captions. It still beats the old light theme's secondary
     grey (2.92:1), but it is under the 4.5:1 AA floor for small text. To take
     Light secondary text to AA (4.74:1), change one line above to:
         --ink-muted: #5a6472;                                            */
}

/* (The old light-theme key lift is gone: every key now carries the system's
   --extruded-small token, which is already theme-aware.) */


/* ============================================================
   Settings modal (§E) — reuses the shared .overlay / .modal chrome.
============================================================ */
.modal-title { font-size: 1.3rem; font-weight: 500; margin-bottom: 20px; }

.set-group { margin-bottom: 20px; }
.set-group:last-child { margin-bottom: 4px; }
.set-group h3 {
  font-size: 0.78rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-secondary);
  margin-bottom: 10px;
}

.seg { display: flex; gap: 6px; }
/* Segmented control: each option is a molded pill; the chosen one is carved
   in. No borders anywhere. */
.seg button {
  flex: 1;
  padding: 10px 6px;
  border: none;
  border-radius: var(--radius-md);
  box-shadow: var(--extruded-small);
  background: var(--surface);
  color: var(--text-primary);
  font-family: var(--font-family);
  font-size: 0.9rem;
  cursor: pointer;
  white-space: nowrap;
}
.seg button.active {
  box-shadow: var(--inset-small);
  color: var(--ink);
  font-weight: 700;
}
.seg button:focus-visible { outline: 2px solid var(--op-color); outline-offset: 2px; }
.seg-dates button { font-size: 0.78rem; padding: 10px 2px; }

.set-select {
  width: 100%;
  padding: 13px 36px 13px 16px;
  border: none;
  border-radius: var(--radius-md);
  box-shadow: var(--inset);
  background-color: var(--surface);
  color: var(--text-primary);
  font-family: var(--font-family);
  font-size: 1rem;
  cursor: pointer;
  appearance: none;
  margin: 0;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238e8e93' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 12px;
}
.set-select:focus-visible { outline: 2px solid var(--op-color); outline-offset: 2px; }
