/* BORDER-BOX, added 2026-09-10 across every live stylesheet that needed it.

   James: "items are bleeding out" on mobile. The cause was one missing line
   repeated nine times: a rule with `width:100%` PLUS padding or a border is
   wider than its container unless box-sizing is border-box, and none of these
   files set it. On a desktop the slack hides it; on a 390px phone it pushes
   the layout sideways.

   Four stylesheets in static/css DO set it globally and every one is loaded
   only by templates/_archive/*, so the rule was being inherited from files
   nothing loads any more. That is how this regressed without a commit
   touching it, and why tests/mobile_bleed_test.py checks the PROPERTY across
   every live stylesheet rather than the one class that was reported.

   Scoped to this file's own prefix rather than a global `*` reset: these
   stylesheets load alongside each other and a blanket reset from one would
   silently change the others. */
[class*="ja-"], [class*="jc-"], [class*="jd-"], [class*="jh-"], [class*="ji-"], [class*="jm-"] { box-sizing: border-box; }

/* Julie — the three themes.
 *
 * James, 2026-09-07: "in Settings we're gonna give the user an option to
 * change the theme, so they can keep the net, they can make it white, they
 * can make it black ... but a theme is not just a background, it's the theme
 * of the site."
 *
 * He is right that it is not a background, and that is what makes it work:
 *   NET    the default. Deep navy, the neural net behind everything.
 *   LIGHT  white ground, black text, an iPhone-blue composer.
 *   BLACK  true black, for OLED and for people who want the room dark.
 *
 * ONE TOKEN SET, THREE VALUES EACH. Anything a theme can change is declared
 * here and nowhere else. A screen is "theme-ready" exactly when it reads
 * these tokens instead of naming a colour, and it is honest to say so screen
 * by screen: as of 2026-09-07 only 17% of the app's colours go through
 * tokens, so themes roll out per surface rather than all at once. A switch
 * that works on three screens and breaks five is worse than no switch, which
 * is why `data-theme` is only offered where the surface has been converted.
 *
 * The attribute lives on <html> and is written before first paint by
 * julie-theme.js, so there is no flash of the wrong theme.
 */

/* ── NET: the default ────────────────────────────────────────────────────── */
:root,
:root[data-theme="net"]{
  --bg:#0A0F1D;
  --bg-deep:#050a14;          /* behind the net, and the page ground */
  --ink:#F1F5F9;
  --muted:#94A3B8;
  --dim:#64748B;
  --accent:#0A84FF;
  --accent-hi:#4DA6FF;
  --panel:rgba(255,255,255,.038);
  --line:rgba(255,255,255,.10);
  --bubble:linear-gradient(180deg,#0A84FF,#0069D9);
  --bubble-ink:#FFFFFF;
  --scrim:10,15,29;           /* the reading scrim's rgb, alpha applied per stop */
  --net-alpha:.14;            /* how strongly the neural net paints */
  --net:1;                    /* 1 shows the canvas, 0 hides it */
  color-scheme:dark;
}

/* ── BLACK: same room, lights off ────────────────────────────────────────── */
:root[data-theme="black"]{
  --bg:#000000;
  --bg-deep:#000000;
  --ink:#F5F5F7;
  --muted:#A1A1A6;
  --dim:#6E6E73;
  --accent:#0A84FF;
  --accent-hi:#4DA6FF;
  --panel:rgba(255,255,255,.05);
  --line:rgba(255,255,255,.12);
  --bubble:linear-gradient(180deg,#0A84FF,#0069D9);
  --bubble-ink:#FFFFFF;
  --scrim:0,0,0;
  /* Brighter on black than on navy: the same alpha over #000 reads as almost
     nothing, because there is no ground colour for it to sit against. */
  --net-alpha:.20;
  --net:1;
  color-scheme:dark;
}

/* ── WARM: the palette taken out of Julie's own photograph ───────────────
 *
 * James, 2026-09-14: "one of the things I wanna do is change the create a
 * theme for the app that matches the color scheme in Julie's photo ... the
 * user's bubble will be something like khaki or brown. Find a color scheme
 * that looks good together and have that as a template we can build off of."
 *
 * SAMPLED, NOT INVENTED. Every value below comes from `static/julie/hero-1.webp`
 * by averaging a named region of the actual image:
 *
 *     blazer, wide band, collar excluded   #2F2922   the ground
 *     studio backdrop, both full edges     #7E776F   the warm grey
 *     tortoiseshell glasses, warmest 8%    #AD7959   the accent
 *     hair highlight                       #5C4D37
 *     skin                                 #AC816E
 *
 * The finding that shaped it: HER PHOTOGRAPH HAS NO BLUE IN IT. An early
 * sample read the blazer as navy, and that was a shadow -- across a wide band
 * it is a warm dark brown. So this theme is the first one here that is not a
 * variation on the app's blue, and the neural net is dimmed rather than
 * recoloured, because a blue net over a brown room is the one thing that
 * would give the whole away.
 *
 * THE USER BUBBLE IS THE GLASSES AT 85%, AND THAT NUMBER IS LOAD-BEARING.
 * At full strength (#AD7959) white body text sits at 3.71:1, under the 4.5:1
 * floor -- the same shape as the verification code that shipped invisible in
 * rule #17a. Darkened to #93674C it reaches 4.90:1, and the gradient's lower
 * stop 6.67:1. Every value here was run through a contrast check before it
 * was written down; the ratios are in the commit.
 */
:root[data-theme="warm"]{
  --bg:#171410;               /* blazer, lifted so panels can sit on it */
  --bg-deep:#100E0B;          /* the page ground, deepest */
  --ink:#F5F0E9;              /* warm off-white -- 16.19:1 on the ground */
  --muted:#A8A096;            /* backdrop, lifted -- 7.11:1 */
  --dim:#7E776F;              /* the backdrop itself -- 4.16:1, large text */
  --accent:#AD7959;           /* the glasses, untouched -- 4.94:1 */
  --accent-hi:#C89170;        /* 6.77:1 */
  /* Warm-tinted rather than plain white at low alpha: neutral white over a
     brown ground greys it out, which is what makes a warm theme look dusty. */
  --panel:rgba(245,240,233,.04);
  --line:rgba(245,240,233,.11);
  --bubble:linear-gradient(180deg,#93674C,#79543E);
  --bubble-ink:#FFFFFF;
  --scrim:23,20,16;
  /* Dimmed, not recoloured. The net is the blue signature of the other three
     themes and at full strength it fights this one; at .06 it is texture. */
  --net-alpha:.06;
  --net:1;
  color-scheme:dark;
}
/* HER side of the conversation is the studio backdrop -- the warm grey James
   asked to keep. 14% is the floor at which it separates from the ground
   (1.16:1); below 12% it reads as nothing at all. */
:root[data-theme="warm"] .jd-seg,
:root[data-theme="warm"] .jd-live .jd-reply{
  background:rgba(126,119,111,.14);
  border-color:rgba(126,119,111,.18);
}

/* ── LIGHT ───────────────────────────────────────────────────────────────── */
:root[data-theme="light"]{
  --bg:#FFFFFF;
  --bg-deep:#F5F5F7;
  --ink:#0B0B0F;              /* his ask: black text */
  --muted:#5B5B66;
  --dim:#8A8A94;
  /* #007AFF, the LIGHT-mode systemBlue. #0A84FF is tuned for a dark ground
     and reads slightly washed on white; Apple ships two values for exactly
     this reason. */
  --accent:#007AFF;
  --accent-hi:#0060DF;
  --panel:rgba(0,0,0,.035);
  --line:rgba(0,0,0,.10);
  --bubble:linear-gradient(180deg,#007AFF,#0064D2);
  --bubble-ink:#FFFFFF;
  --scrim:255,255,255;
  /* THE NET IS OFF ON LIGHT, not merely faint. It is a light-on-dark lattice;
     over white it is either invisible or, turned up enough to see, dirty. The
     aurora is the better companion for a white ground and its markup is still
     in the page for that reason. */
  --net-alpha:0;
  --net:0;
  color-scheme:light;
}

/* The canvas obeys the token rather than each page deciding. */
:root[data-theme="light"] #neuralBg,
:root[data-theme="light"] .jd-neural{ display:none }

/* Page ground. Declared here so a theme change repaints the body even on a
   surface that has not been converted yet -- it will look wrong in places,
   but it will not look BROKEN, which is the difference between "not done"
   and "shipped badly". */
html[data-theme]{ background:var(--bg-deep); }

/* ── The picker in Settings ──────────────────────────────────────────────── */
.jth-row{ display:flex; gap:10px; margin-top:4px }
.jth{
  flex:1 1 0; min-width:0; cursor:pointer; padding:0; border-radius:13px;
  border:1px solid var(--line, rgba(255,255,255,.10));
  background:transparent; display:flex; flex-direction:column;
  align-items:center; gap:7px; padding:9px 6px 8px;
  font:inherit; color:var(--muted, #94A3B8); font-size:.76rem;
  transition:border-color .16s ease, color .16s ease;
}
.jth[aria-checked="true"]{
  border-color:var(--accent, #0A84FF); color:var(--ink, #F1F5F9);
  box-shadow:0 0 0 3px rgba(10,132,255,.16);
}
/* Each swatch is painted in its OWN palette rather than the current one. The
   point of a swatch is to answer "what would it look like", and one that
   inherits the theme you are already in answers nothing. */
.jth-sw{
  width:100%; height:34px; border-radius:8px; overflow:hidden;
  display:flex; flex-direction:column; justify-content:center; gap:4px;
  padding:0 8px;
}
.jth-sw i{ display:block; height:4px; border-radius:99px }
.jth-net  { background:linear-gradient(160deg,#0A0F1D,#111a2e) }
.jth-net   i:nth-child(1){ background:#F1F5F9; width:70% }
.jth-net   i:nth-child(2){ background:#0A84FF; width:44% }
.jth-light{ background:#FFFFFF; border:1px solid rgba(0,0,0,.10) }
.jth-light i:nth-child(1){ background:#0B0B0F; width:70% }
.jth-light i:nth-child(2){ background:#007AFF; width:44% }
.jth-black{ background:#000000; border:1px solid rgba(255,255,255,.16) }
.jth-black i:nth-child(1){ background:#F5F5F7; width:70% }
.jth-black i:nth-child(2){ background:#0A84FF; width:44% }
/* Auto (2026-10-05): half white, half black, a diagonal day and night. */
.jth-auto{ --theme-independent:1; background:linear-gradient(135deg,#FFFFFF 0 50%,#000000 50% 100%); border:1px solid rgba(128,128,128,.35) }
.jth-auto i:nth-child(1){ background:#6B7280; width:70% }
.jth-auto i:nth-child(2){ background:#0A84FF; width:44% }
/* The swatch is the theme in miniature: her blazer, her off-white, her
   glasses. Anyone who has looked at the portrait should recognise it. */
.jth-warm{
  /* A swatch shows its own theme in miniature -- repainting it for the
     ACTIVE theme would make every swatch look the same and stop it being a
     swatch. .jth-black beside it is the same idea. */
  --theme-independent:1;
  background:#171410; border:1px solid rgba(245,240,233,.16) }
.jth-warm  i:nth-child(1){ background:#F5F0E9; width:70% }
.jth-warm  i:nth-child(2){ background:#AD7959; width:44% }

/* THE PREVIEW CARD inside a tool card. Its ground is `rgba(0,0,0,.18)` -- a
   translucent WASH, not an opaque plate -- so under Light it resolves to a
   pale grey and the near-white type written for the dark theme sits on it
   invisibly: #CBD5E1 body, #E2E8F0 on the strongs, and an amber #FCD34D title
   on an amber-tinted card. Found by tests/theme_light_surface_test.py, which
   flags a chrome colour with no light rule; three of its nine findings were
   real and these are them. The other six carry their own ground in both
   themes and are marked `theme-independent` where they live. */
:root[data-theme="light"] .jc-preview{
  background:rgba(15,23,42,.05);
  border-color:rgba(15,23,42,.10);
  color:var(--ink);
}
:root[data-theme="light"] .jc-preview strong{ color:var(--ink) }
/* The amber title is legible on the dark card and washes out on the light
   one; the darker amber keeps the "this is a preview" signal without asking
   anyone to read pale yellow on cream. */
:root[data-theme="light"] .jc-preview-card .jc-title{ color:#B45309 }

/* ── LIGHT: the shared module patterns ────────────────────────────────────
   The modules are already heavily tokenised -- health has 139 token
   references, finances 112, email 55 -- so most of each screen follows the
   theme for free. What does not are a handful of patterns that recur in ALL
   of them, and they are fixed once here rather than converted eight times.
   The dead `wellness.html` (nothing renders it, see main.py) is not in scope.

   Everything below was found by measuring the rendered page under Light and
   reading the computed colours, not by reading the stylesheets. */

/* ACTION LINKS. `.btn` resolves to --muted, which on a dark ground reads as a
   quiet action and on white reads as a DISABLED one: rgb(91,91,102) next to
   black body text says "you cannot press this". Set a budget, Add, Add
   account are the primary things to do on those screens. */
:root[data-theme="light"] .btn{
  color:var(--accent);
}
:root[data-theme="light"] .btn:hover{ color:var(--accent-hi) }

/* FORM CONTROLS. A select carried background rgba(0,0,0,.26) -- a dark plate
   sitting on white, inherited from a rule written for the dark theme. Native
   controls need an explicit ground in both themes or they inherit whichever
   one they were designed against. */
:root[data-theme="light"] select,
:root[data-theme="light"] input,
:root[data-theme="light"] textarea{
  background:#FFFFFF;
  color:var(--ink);
  border:1px solid rgba(0,0,0,.14);
}
:root[data-theme="light"] select:focus,
:root[data-theme="light"] input:focus,
:root[data-theme="light"] textarea:focus{
  border-color:var(--accent);
  outline:none;
  box-shadow:0 0 0 3px rgba(0,122,255,.16);
}
:root[data-theme="light"] ::placeholder{ color:var(--dim) }

/* The floating add button was light blue on light blue: legible as a shape,
   not as a control. */
:root[data-theme="light"] .addfab{
  background:var(--accent);
  color:#FFFFFF;
  border-color:transparent;
  box-shadow:0 6px 18px rgba(0,122,255,.32);
}

/* Module chrome that was tuned against navy. */
:root[data-theme="light"] .jm-home{ color:var(--muted) }
:root[data-theme="light"] .jm-home:hover{ color:var(--ink) }
:root[data-theme="light"] .top{ background:var(--bg) }

/* The nav rail and history panel are dark surfaces by construction; on light
   they become white panels with a border rather than floating dark slabs. */
:root[data-theme="light"] .jn-panel,
:root[data-theme="light"] .jh-panel{
  background:#FFFFFF;
  border-color:rgba(0,0,0,.12);
  box-shadow:0 18px 50px rgba(15,23,42,.16);
}
:root[data-theme="light"] .jn-panel a,
:root[data-theme="light"] .jh-panel a{ color:var(--ink) }
:root[data-theme="light"] .jn-toggle,
:root[data-theme="light"] .jh-toggle{ color:var(--muted) }

/* The module dock is her, over a light page. Same treatment. */
:root[data-theme="light"] .jd-sheet{ background:rgba(255,255,255,.94) }
:root[data-theme="light"] .jd-sheet::before{ background:rgba(255,255,255,.72) }
:root[data-theme="light"] .jd-head{ background:#FFFFFF }
:root[data-theme="light"] .jd-name{ color:var(--ink) }
:root[data-theme="light"] .jd-grab{ background:rgba(0,0,0,.22) }

/* The news reader's header floats over a full-bleed photo and carries a dark
   text-shadow so the title reads against any picture. On a light ground with
   no photo behind it that shadow is a smudge around the type. The reader
   itself is unaffected -- it is still photos under a floating header there. */
:root[data-theme="light"] .wrap > .top{ text-shadow:none }
:root[data-theme="light"] .nw-story .body{ text-shadow:none }

/* ── LIGHT: the shared chrome ──────────────────────────────────────────────
   THE INCIDENT. Light shipped 2026-09-07 having been verified on the arrival
   screen and the module bodies, and the work plan then recorded it as still
   "not started" because what had never been done was a pass over the CHROME:
   the dock, the nav, the history panel, the settings modal and the small
   overlays that ride on every page. Those live in their own stylesheets, and
   every one of them names its colours as literals rather than reading tokens.

   The worst of it was invisible rather than merely ugly. The block above
   turns `.jd-sheet` white, and `julie-dock.css` sets `.jd-reply{color:#F1F5F9}`
   -- so on light, on every module page, JULIE'S ANSWERS WERE WHITE TEXT ON A
   WHITE SHEET. Measured 19.64:1 the right way round on `--ink` and 1.04:1 as
   it actually rendered. The user's own blue bubble survived, so the dock
   looked like a conversation with one side missing.

   WHY LITERALS HERE RATHER THAN CONVERTING THOSE FILES TO TOKENS. Converting
   is the house pattern and it is the right end state, but the literals and the
   tokens are NOT the same values -- `.jd-starter` is #CBD5E1 where `--muted`
   is #94A3B8, `.jd-name` is #F8FAFC where `--ink` is #F1F5F9. A straight swap
   would have quietly restyled the dark themes James has already signed off in
   order to fix a light one he has not seen yet, which is the wrong trade. So
   the dark files keep their literals untouched and light is stated here, in
   the file that already owns every other light override. The token conversion
   is a separate pass, listed in the work plan.

   Load order does not help us: _pwa_head.html is included at the TOP of every
   page, so julie-theme.css is parsed BEFORE julie-dock.css and the rest. Every
   rule below therefore has to win on SPECIFICITY alone. `:root[data-theme=
   "light"] .x` is (0,3,0) against a bare `.x` at (0,1,0), which is why the
   pattern works at all -- but it also means a compound selector in a chrome
   file needs a compound selector here, and a bare class will silently lose.
   That is exactly the failure the accompanying test scans for.

   Everything below was derived by parsing the live stylesheets for near-white
   text and dark fills that have no light counterpart, not by reading them and
   forming an impression. See tests/theme_light_surface_test.py, which is the
   same scan run as an assertion. */

/* THE MODULE DOCK. The panel that opens on Finances, Health, News and the
   rest, and the surface the invisible-text bug was on. */
:root[data-theme="light"] .jd-reply,
:root[data-theme="light"] .jd-reply strong,
:root[data-theme="light"] .jd-reply h3{ color:var(--ink) }
:root[data-theme="light"] .jd-reply a{ color:var(--accent-hi) }
:root[data-theme="light"] .jd-reply code{ background:rgba(0,0,0,.055) }
:root[data-theme="light"] .jd-reply pre{
  background:rgba(0,0,0,.045); border-color:rgba(0,0,0,.08);
}
:root[data-theme="light"] .jd-reply th{
  color:var(--accent-hi); background:rgba(0,0,0,.035);
}
:root[data-theme="light"] .jd-turn{ border-bottom-color:rgba(0,0,0,.08) }
/* The composer. Its tint is `rgba(11,17,32,.42)` -- a dark plate deliberately
   chosen so a near-black page would not turn milky. On white it is a charcoal
   box around white placeholder text. */
:root[data-theme="light"] .jd-rail{
  background:
    linear-gradient(rgba(255,255,255,.9), rgba(255,255,255,.55)),
    rgba(255,255,255,.62);
  border-color:rgba(0,0,0,.10);
}
:root[data-theme="light"] .jd-rail:focus-within{ border-color:var(--accent) }
:root[data-theme="light"] .jd-input{ color:var(--ink) }
:root[data-theme="light"] .jd-input::placeholder{ color:var(--dim) }
:root[data-theme="light"] .jd-send:hover:not(:disabled){
  background:rgba(0,0,0,.06); color:var(--ink);
}
:root[data-theme="light"] .jd-send:disabled{
  background:rgba(0,0,0,.05); color:var(--dim);
}
:root[data-theme="light"] .jd-starter{
  color:var(--ink); background:rgba(0,0,0,.05);
}
:root[data-theme="light"] .jd-starter:hover,
:root[data-theme="light"] .jd-starter:focus-visible{
  color:var(--ink); background:rgba(0,122,255,.12);
}
:root[data-theme="light"] .jd-empty,
:root[data-theme="light"] .jd-note{ color:var(--muted) }
:root[data-theme="light"] .jd-face{ background:#E8E8ED }
:root[data-theme="light"] .jd-head::after{
  background:linear-gradient(to bottom, #FFFFFF, rgba(255,255,255,0));
}
:root[data-theme="light"] .jd-pill{
  background:rgba(255,255,255,.94); border-color:rgba(0,0,0,.12);
}
/* `a.jd-att:hover`, with the element qualifier, because that is how
   julie-dock.css writes it -- and a bare `.jd-att:hover` here would lose the
   specificity comparison to it (0,3,0 against the dark rule's 0,2,1... which
   it would in fact win, but only until somebody adds a class to the anchor).
   Mirror the dark selector exactly rather than reasoning about the margin.
   The test caught this one: the rest state was covered and the hover was not,
   which on a white sheet is a link that vanishes when you point at it. */
:root[data-theme="light"] .jd-att{ color:var(--ink) }
:root[data-theme="light"] a.jd-att:hover{ color:var(--accent-hi) }
:root[data-theme="light"] .jd-att img,
:root[data-theme="light"] .jd-att-icon{ background:rgba(0,0,0,.05) }
:root[data-theme="light"] .jd-said-attach{
  background:rgba(0,122,255,.10); color:#0050B3;
  border-color:rgba(0,122,255,.30);
}
:root[data-theme="light"] .jd-sheet::-webkit-scrollbar-thumb{
  background:rgba(0,0,0,.20);
}

/* THE NAV RAIL. The panel itself is handled above; these are the parts of it
   that only appear on hover, which is how they were missed. */
:root[data-theme="light"] .jn-toggle:hover,
:root[data-theme="light"] .jn-toggle[aria-expanded="true"],
:root[data-theme="light"] .jn-panel a:hover{ color:var(--ink) }
:root[data-theme="light"] .jn-scrim{ background:rgba(15,23,42,.28) }
/* `.jn-danger` carries !important, so this has to as well. It is also the
   worst contrast in the app on white: #F87171 measures 2.77:1 and its hover
   #FCA5A5 measures 1.90:1, i.e. the hover state is LESS readable than the
   rest state. #D70015 is Apple's light-mode systemRed, 5.38:1. */
:root[data-theme="light"] .jn-danger{ color:#D70015 !important }
:root[data-theme="light"] .jn-danger:hover{
  background:rgba(215,0,21,.10) !important; color:#9F0010 !important;
}

/* THE HISTORY PANEL and its menus. */
:root[data-theme="light"] .jh-toggle:hover,
:root[data-theme="light"] .jh-toggle[aria-expanded="true"],
:root[data-theme="light"] .jh-item:hover,
:root[data-theme="light"] .jh-seeall:hover,
:root[data-theme="light"] .jh-x:hover,
:root[data-theme="light"] .jt-title:hover,
:root[data-theme="light"] .jt-title.jt-editing,
:root[data-theme="light"] .jh-menu button:hover{ color:var(--ink) }
:root[data-theme="light"] .jh-heading,
:root[data-theme="light"] .jh-item,
:root[data-theme="light"] .jh-menu button{ color:var(--muted) }
:root[data-theme="light"] .jh-new:hover{ color:var(--accent-hi) }
:root[data-theme="light"] .jh-menu{
  background:#FFFFFF; border-color:rgba(0,0,0,.12);
  box-shadow:0 12px 34px rgba(15,23,42,.16);
}
:root[data-theme="light"] .jh-scrim,
:root[data-theme="light"] .jh-modal{ background:rgba(15,23,42,.34) }
:root[data-theme="light"] .jh-card{
  background:#FFFFFF; color:var(--ink); border-color:rgba(0,0,0,.12);
}
:root[data-theme="light"] .jh-search{
  background:#FFFFFF; color:var(--ink); border-color:rgba(0,0,0,.14);
}

/* THE SETTINGS MODAL. Loaded by julie-settings-modal.js at runtime, so its
   stylesheet lands after this one in the cascade -- specificity again, not
   order. This is also the one surface where somebody is guaranteed to be
   looking, because it is where the theme picker lives: choose Light here and
   the dialog you chose it in is the first thing you judge it by. */
:root[data-theme="light"] .jsm,
:root[data-theme="light"] .jst-modal{ background:rgba(15,23,42,.38) }
:root[data-theme="light"] .jsm-card,
:root[data-theme="light"] .jst-modal-card{
  background:#FFFFFF; color:var(--ink); border-color:rgba(0,0,0,.12);
  box-shadow:0 24px 60px rgba(15,23,42,.18);
}
:root[data-theme="light"] .jst-tabs{ background:#F2F2F5 }
:root[data-theme="light"] .jsm-x:hover,
:root[data-theme="light"] .jst-tab:hover,
:root[data-theme="light"] .jst-row:hover,
:root[data-theme="light"] .jst-chip:hover,
:root[data-theme="light"] .jst-details summary:hover,
:root[data-theme="light"] .jst-btn.secondary:hover{ color:var(--ink) }
:root[data-theme="light"] .jst-btn.ghost:hover{ color:var(--accent-hi) }
:root[data-theme="light"] .jst-check,
:root[data-theme="light"] .jst-row,
:root[data-theme="light"] .jst-toggle-k,
:root[data-theme="light"] .jst-time{ color:var(--muted) }
:root[data-theme="light"] .jst-input{ color:var(--ink) }

/* ATTACHMENTS. The picker sheet and the chips a file leaves in the composer. */
:root[data-theme="light"] .ja-usage:hover{ color:#1C1C1E }
/* White on the accent-blue button reads on either ground; stated so the
   light palette is a decision, not an omission. */
:root[data-theme="light"] .jd .fresh-actions .fresh-go{ color:#fff }
:root[data-theme="light"] .ja-sheet{
  background:#FFFFFF; border-color:rgba(0,0,0,.12);
}
:root[data-theme="light"] .ja-chip{
  background:rgba(255,255,255,.94); border-color:rgba(0,0,0,.12);
}
:root[data-theme="light"] .ja-chip img,
:root[data-theme="light"] .ja-input{ background:#F2F2F5 }
:root[data-theme="light"] .ja-input{ color:var(--ink) }
:root[data-theme="light"] .ja-name,
:root[data-theme="light"] .ja-row{ color:var(--muted) }
:root[data-theme="light"] .ja-clip:hover,
:root[data-theme="light"] .ja-row[aria-checked="true"] .ja-lbl{ color:var(--ink) }
:root[data-theme="light"] .ja-veil{ background:rgba(255,255,255,.78) }
:root[data-theme="light"] .ja-size-top{ color:var(--ink) }
:root[data-theme="light"] .ja-size-sub{ color:var(--muted) }
:root[data-theme="light"] .ja-size-bar{ background:rgba(0,0,0,.10) }
:root[data-theme="light"] .ja-veil span{
  background:rgba(255,255,255,.94); color:var(--ink);
}

/* THE IDLE WARNING and the tour bubble. Both are full-attention overlays: an
   unreadable one is not a cosmetic problem, it is a person who cannot tell
   they are about to be signed out. */
:root[data-theme="light"] .ji-warn{ background:rgba(15,23,42,.36) }
:root[data-theme="light"] .ji-card{
  background:#FFFFFF; color:var(--ink); border-color:rgba(0,0,0,.12);
}
:root[data-theme="light"] .ji-body b,
:root[data-theme="light"] .ji-go:hover{ color:var(--ink) }
:root[data-theme="light"] .jt-bubble{
  background:#FFFFFF; color:var(--ink); border-color:rgba(0,0,0,.12);
}
:root[data-theme="light"] .jt-skip:hover{ color:var(--ink) }

/* The citation list, the answer cards, the read-aloud
   controls, the mic, the swipe row and the step ticker. Small surfaces, but
   every one of them rides on the dock or the transcript, so all of them are
   over a white ground the moment Light is on. */
:root[data-theme="light"] .js-src-head,
:root[data-theme="light"] .js-src-title,
:root[data-theme="light"] .jt-steps-label,
:root[data-theme="light"] .jt-step{ color:var(--muted) }
:root[data-theme="light"] .js-src-head:hover{ color:var(--ink) }
:root[data-theme="light"] .js-src-ic{ background:#E4E4EA }
:root[data-theme="light"] .js-src-ic::before{ color:var(--muted) }
:root[data-theme="light"] .js-cite:hover{ color:var(--accent-hi) }
:root[data-theme="light"] .jsp-row .js-src-list{
  background:rgba(255,255,255,.97);
}
:root[data-theme="light"] .jc-title,
:root[data-theme="light"] .jc-answers button,
:root[data-theme="light"] .jc-consent.jc-answered .jc-title{ color:var(--ink) }
/* The consent card's amber title is a warning colour: #FCD34D is 1.44:1 on
   white, which is not a warning, it is a blank space. */
:root[data-theme="light"] .jc-consent .jc-title{ color:#8A5A00 }
:root[data-theme="light"] .jsp-btn:hover,
:root[data-theme="light"] .jsp-auto:hover,
:root[data-theme="light"] .jmic:hover{ color:var(--ink) }
:root[data-theme="light"] .jsp-auto{ background:rgba(255,255,255,.86) }
:root[data-theme="light"] .jsp-btn.ok{ color:#137333 }
:root[data-theme="light"] .jsw-front{ background:var(--bg) }
:root[data-theme="light"] .jsw-undo{
  background:rgba(255,255,255,.96); color:var(--ink);
  border:1px solid rgba(0,0,0,.12);
}
/* Markdown's semantic colours. Both were picked to glow on navy: #4ade80
   measures 1.74:1 on white and #fbbf24 1.67:1, so "good" and "warning" arrive
   as two shades of nothing. The replacements are 5.95:1 and 5.93:1. */
:root[data-theme="light"] .md-c-green{ color:#137333 }
:root[data-theme="light"] .md-c-amber{ color:#8A5A00 }

/* Drawn icons for close, add and the day arrows (2026-10-08, polish pass).
   They replaced typed characters (×, +, ‹, ›), which render as a different
   glyph on every font and sit off-centre in a round button. Sized 1em so
   each button's existing font-size still decides how big it is. */
.ic-x, .ic-plus, .ic-chev { display: block; margin: auto; flex: none; pointer-events: none; }

/* ═══ MOVING BETWEEN PAGES (2026-10-08, polish pass) ═════════════════════════
   James: "It just doesn't feel like modern advanced AI." Every module change
   was a hard cut: white-out, then the next page. Cross-document view
   transitions let the browser fade the old page out and the new one in
   (Safari 18.2+, Chrome 126+); anything older simply navigates as before, so
   nothing depends on it. Opacity and a 6px rise only, under 250 ms, and
   nothing at all when the phone asks for reduced motion. */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: jvt-out 160ms cubic-bezier(.4,0,1,1) both; }
::view-transition-new(root) { animation: jvt-in 230ms cubic-bezier(.22,1,.36,1) both; }
@keyframes jvt-out { to { opacity: 0; } }
@keyframes jvt-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}
