/* ITZA Diary - design system.
 *
 * A writing environment for people who take their own writing seriously. It
 * must feel like a diary, not a dashboard, and not like a crypto product. A
 * person who does not know what a blockchain is must never meet one.
 *
 * NO WEB FONTS. cdn.itzalabs.com is the only permitted remote origin, and
 * that is for the platform library alone - no font CDN, no icon font, no
 * external stylesheet. These stacks use what is already on the device, which
 * costs nothing and never blocks first paint.
 */

:root {
  /* Spec 17.2. Two values are deliberately darker than the source palette -
   * see the note below - and everything else is verbatim. */
  --ink: #12161c;      /* body text, dominant by area */
  --paper: #fbfaf7;    /* the reading surface */
  --navy: #0b2a4a;     /* headers, structure */
  --gold: #f5c711;     /* THE SINGLE ACCENT: verification, provenance only */
  --rule: #e2ded6;     /* hairlines */

  /* CONTRAST CORRECTIONS, against --paper, per the 17.5 quality floor
   * ("WCAG 2.1 AA contrast verified against the reading surface
   * specifically"). The source values fail that floor for body-size text:
   *
   *   --muted #6b7280 -> 4.26:1   below the 4.5:1 minimum
   *   --blue  #0c7ffb -> 3.70:1   below the 4.5:1 minimum
   *
   * Metadata and links are both normal-size text on the reading surface, so
   * both need 4.5:1. The corrected values below measure 5.0:1 and are within
   * a hair of the originals in hue. The original --blue is kept as
   * --blue-bright for large UI elements and focus rings, where 3:1 is the
   * applicable threshold and it passes comfortably. */
  --muted: #666d79;        /* metadata, timestamps - 5.0:1 */
  --blue: #0b6ad4;         /* interactive, links, actions - 5.0:1 */
  --blue-bright: #0c7ffb;  /* large UI and focus only, never body text */

  /* 42rem at the 20px body is ~65 characters a line - the classic reading
   * measure, and within a few pixels of Medium's 680px column. The first
   * version shipped 34rem (~50 chars): correct for a printed novella,
   * visibly cramped for a web article (user comparison, 2026-08-05). The
   * measure is in rem on purpose: the text-size setting scales the root, so
   * characters-per-line stays constant at every size. */
  --measure: 42rem;        /* reader-adjustable, see 17.3 */
  --body-size: 1.25rem;    /* rem ON PURPOSE: the body rides the root scale */
  --leading: 1.65;

  --serif-body: 'Iowan Old Style', 'Palatino Linotype', Palatino, Charter,
    'Bitstream Charter', 'Source Serif Pro', serif;
  --serif-display: 'Hoefler Text', 'Baskerville Old Face', Baskerville,
    'Times New Roman', serif;
  --sans: system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
}

/* Reader controls (17.3): the reading experience is the product, so the
 * reader sets the scale and the line width. Applied on <html> by an inline
 * script BEFORE first paint, so articles inherit them without JavaScript and
 * without a flash at the wrong size. */
/* The type scale moves the ROOT SIZE as well as the article body. The first
 * version scaled only --body-size, so dates, bylines, menus and every other
 * rem-sized surface ignored the reader's setting entirely (reported
 * 2026-08-02: "it doesn't seem to be affecting the date and other areas").
 * rem inherits from here, so now everything breathes together. */
/* THE VIEWPORT GUARD. `clip` clips horizontal overflow WITHOUT creating a
 * scroll container, so position:sticky keeps working - and no single
 * overflowing row can ever widen the mobile layout viewport again and drag
 * every fixed overlay off the screen with it. */
html, body { overflow-x: hidden; overflow-x: clip; }

/* THE TEXT SCALE (calibration spec, 2026-08-13). One root multiplier
 * drives every rem in the app - seven steps, 0.9x to 2x, chosen on a
 * live sample at first run and mirrored in Settings. NEVER a px root:
 * calc(100% * scale) keeps the browser's and the OS's own text scaling
 * underneath instead of discarding them. The 200ms ease previews a step
 * change as one movement; reduced motion snaps. The old data-size px
 * roots are gone - they were exactly the px-root mistake. */
html {
  font-size: calc(100% * var(--text-scale, 1));
  transition: font-size 200ms ease;
  -webkit-text-size-adjust: 100%;
}
html[data-motion='reduce'] { transition: none; }
/* Narrow IS the old default - anyone who liked the tighter column picks it
 * in Settings and gets exactly what shipped before. */
html[data-measure='narrow'] { --measure: 34rem; }
html[data-measure='wide'] { --measure: 48rem; }
html[data-justify='1'] .article p { text-align: justify; hyphens: auto; }

/* Reading surface themes. Dark keeps the same hierarchy rather than inverting
 * it, and gold stays gold because it means one thing in both (17.2). Each is
 * checked for AA contrast against its own paper, not against the light one. */
html[data-theme='dark'] {
  --ink: #e8e6e1;
  --paper: #14171c;
  --navy: #a9c6e8;
  --rule: #2b3038;
  --muted: #9aa1ad;       /* 7.0:1 on this paper */
  --blue: #6fb3ff;        /* 6.9:1 */
  --blue-bright: #8ec6ff;
}

html[data-theme='hc'] {
  --ink: #000000;
  --paper: #ffffff;
  --navy: #000000;
  --rule: #000000;
  --muted: #3a3a3a;
  --blue: #0033cc;
  --gold: #a06f00;        /* the mark still reads at high contrast */
}

/* A reader who asked for less motion gets it whether or not the OS knows. */
html[data-motion='reduce'] *,
html[data-motion='reduce'] *::before,
html[data-motion='reduce'] *::after {
  animation-duration: 0.001ms !important;
  transition-duration: 0.001ms !important;
}

/* ---------------------------------------------------------------------
   Screens, modal, toast
   --------------------------------------------------------------------- */

.screen { display: none; }
.screen.on { display: block; }
.screen:focus { outline: none; }

.apphead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 1.25rem;
  border-bottom: 1px solid var(--rule);
  font-family: var(--sans);
}

/* The wordmark - the HOUSE STANDARD, mirrored from the marketing site's
 * stylesheet: ITZA in corporate blue (600) runs straight into the product
 * word in gold (800), no space. On light paper the gold deepens to #ad8009
 * to clear the contrast floor (brand gold on near-white is illegible - the
 * site's own comment); on dark it brightens back to brand gold, and ITZA
 * lifts to the site's dark-context blue. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--serif-display);
  font-size: 1.05rem;
  text-decoration: none;
  letter-spacing: -0.01em;
}

.brand-mark { width: 22px; height: 22px; }
.wm { white-space: nowrap; }
.wm-itza { color: #0c7ffb; font-weight: 600; }
/* The product word is GOLD - metal, not mustard. A flat #ad8009 read as
   brown (user report, 2026-08-08); real gold is a gradient: deep gold into
   a bright specular band and back, the same play of light the pyramid mark
   has. The solid color stays as the fallback for engines without
   background-clip: text. */
.wm-prod { color: #ad8009; font-weight: 800; }
@supports (-webkit-background-clip: text) or (background-clip: text) {
  .wm-prod {
    background-image: linear-gradient(105deg,
      #8a6206 0%, #c99a10 22%, #f9df6d 40%, #f5c711 52%,
      #a67c08 72%, #e0b312 88%, #b98a0a 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}
html[data-theme='dark'] .wm-itza { color: #4d9bff; }
html[data-theme='dark'] .wm-prod { color: #f5c711; }
@supports (-webkit-background-clip: text) or (background-clip: text) {
  html[data-theme='dark'] .wm-prod {
    background-image: linear-gradient(105deg,
      #b98a0a 0%, #f5c711 25%, #ffe98a 42%, #f5c711 55%,
      #c99a10 75%, #ffd83d 90%, #e0b312 100%);
    color: transparent;
  }
}

.appnav { display: flex; align-items: center; gap: 1rem; font-size: 0.82rem; }
.appnav a { color: var(--blue); text-decoration: none; padding-bottom: 2px; }
.appnav a:hover { text-decoration: underline; }
.appnav a[aria-current='page'] { color: var(--navy); font-weight: 600; border-bottom: 2px solid var(--navy); }
.appnav a[hidden] { display: none; }

/* Visible keyboard focus everywhere (17.5). */
/* Focus, in two registers.
 *
 * CONTROLS - things you press - keep a visible ring: keyboard users navigate
 * by it, and 3:1 contrast against any surface is the requirement the bright
 * blue exists to meet.
 *
 * FIELDS - things you type into - show focus with the caret they already
 * have, plus a quiet border shift. A ring around a text field says "form";
 * around the writing surface it says "you are filling in a box" on the exact
 * pixels where the design is supposed to say "you are writing the page". */
a:focus-visible, button:focus-visible, select:focus-visible,
[role="button"]:focus-visible {
  outline: 2px solid var(--blue-bright);
  outline-offset: 2px;
}

input:focus-visible, textarea:focus-visible {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(12, 127, 251, 0.13);
}

/* The writing surface itself: no ring, no glow, no box. The caret, the
 * placeholder disappearing and the toolbar arriving are the focus state.
 * This is also what keeps the editor visually identical to the published
 * page - same measure, same type, no furniture around it. */
.editor:focus-visible,
.editor:focus,
.title-input:focus-visible {
  outline: none;
  border-color: transparent;
  box-shadow: none;
}

.iconbtn {
  background: transparent;
  border: 1px solid var(--rule);
  color: var(--muted);
  border-radius: 4px;
  font-size: 1rem;
  line-height: 1;
  padding: 0.3rem 0.5rem;
  cursor: pointer;
}

#scrim {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(11, 42, 74, 0.45);
  /* ABOVE #screen-onboard.on (z 50, ported verbatim from Chess). At 40 the
   * signup confirmation modal opened UNDERNEATH the opaque sign-in card:
   * "Create account" appeared to do absolutely nothing, while its modal sat
   * waiting where no click could reach it. Chess's own scrim is 60 for the
   * same reason - the modal layer must beat every screen, onboarding
   * included. */
  z-index: 60;
  padding: 1rem;
  overflow-y: auto;
}

/* Dialogs sit CENTRED in the room, not pinned to the ceiling with a
   mile of scrim below (owner screenshot, 2026-08-12). `safe center`
   keeps a dialog taller than the screen reachable from its top instead
   of clipping both ends. */
#scrim.on { display: flex; align-items: safe center; justify-content: center; }

#modal {
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg, 16px);
  width: 100%;
  max-width: 32rem;
  margin: 3vh auto;
  padding: 1.25rem;
  font-family: var(--sans);
  /* THE APP'S READING SIZE, not a smaller one. Dialogs always rode the
   * --text-scale root like everything else, but their 0.88rem base sat
   * ~20% under the body text at every step - which at the larger steps
   * reads exactly as "this popup ignores my text size" (owner,
   * 2026-08-22, the receipt consent). Same rem, same scale, same size
   * as the writing around it. */
  font-size: 1rem;
  /* A dialog taller than the screen scrolls INSIDE itself - it must never
   * push its own buttons off the bottom. */
  max-height: calc(100vh - 6vh - 2rem);
  overflow-y: auto;
}

.mhead {
  font-family: var(--serif-display);
  font-size: 1.4rem;
  color: var(--navy);
  margin-bottom: 1rem;
}

/* THE BUTTON ROW HAS A LAYOUT. Bare inline-blocks flowed left and
 * wrapped ragged - two mismatched pills stacked against the left edge
 * (owner screenshot, 2026-08-12). Wide screens: a right-aligned row,
 * primary rightmost. Narrow screens: stacked full width, primary on
 * top - dialogs write ghost-then-primary in the DOM, so the reversed
 * column puts the strongest action first. */
.mbtns {
  margin-top: 1.25rem;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}
@media (max-width: 480px) {
  .mbtns { flex-direction: column-reverse; align-items: stretch; }
}

/* Stacked full-width dialog buttons - the strongest action first, each on
 * its own line, breathing room between them. */
.mbtns-stack { flex-direction: column; align-items: stretch; }
.mbtns-stack .btn.wide + .btn.wide { margin-top: 0; }

/* Body copy inside a dialog. Bare <p> in a modal inherits the reading
 * serif at reading size, which crowds a small card. */
.mtext {
  font-family: var(--sans);
  font-size: 0.92rem;
  line-height: 1.55;
  color: var(--ink);
  margin: 0 0 0.8rem;
  max-width: none;
}
.mtext b { color: var(--navy); }

/* The quiet third choice under a dialog's buttons. .linklike is scoped to
 * the sign-in card, so a modal needs its own - without this it rendered as
 * a full navy button jammed under the row (user report, 2026-08-07). */
.mlink {
  display: block;
  margin: 0.95rem auto 0;
  padding: 0;
  background: none;
  border: none;
  color: var(--muted);
  font-family: var(--sans);
  font-size: 0.78rem;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.mlink:hover { color: var(--ink); }

#toast {
  position: fixed;
  left: 50%;
  bottom: 1.5rem;
  transform: translateX(-50%) translateY(1rem);
  background: var(--navy);
  color: var(--paper);
  font-family: var(--sans);
  font-size: 0.82rem;
  line-height: 1.4;
  padding: 0.7rem 1rem;
  border-radius: 6px;
  max-width: min(34rem, calc(100vw - 2rem));
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease, transform 0.18s ease;
  /* Above every screen AND the modal scrim (60) - a toast is feedback about
   * the thing you just did and must never be buried under it. Chess puts its
   * toasts at 90 for the same reason. At 50 this TIED #screen-onboard.on and
   * only won by DOM order, which is a coin toss nobody should bet UI on. */
  z-index: 90;
}

#toast.on { opacity: 1; transform: translateX(-50%) translateY(0); }
/* A toast CARRYING AN ACTION must accept the tap - the base rule lets
   touches pass through pure feedback, which silently made the Undo
   button decoration on a real phone (owner, 2026-08-11): a synthetic
   .click() in the test fired the handler anyway, so only a hit-test
   could have caught it. */
#toast.has-act { pointer-events: auto; }

/* ---------------------------------------------------------------------
   Sign in - PORTED 1:1 FROM ITZA CHESS, which ported it from ITZA CHAT
   ---------------------------------------------------------------------

   These values are Chess's, verbatim, not a reinterpretation of them. The
   sign-in screen is the same screen in every ITZA app on purpose: it is the
   same account, and a person who has signed in to one should recognise the
   next one immediately.

   SELF-CONTAINED. The whole palette is scoped to #screen-onboard rather than
   :root, so the card cannot be disturbed by the app's own theme and cannot
   disturb it either. Light is the default; dark follows the app's theme
   attribute, which here is html[data-theme] rather than Chess's
   html[data-bg] - that mapping is the only change made to any of it.
   --------------------------------------------------------------------- */

#screen-onboard{
  --ob-blue:#0c7ffb; --ob-blue-soft:#1d6fd0; --ob-gold:#f5c711;
  --ob-acc:#0c7ffb; --ob-acc-ink:#ffffff;
  --ob-bg:#f3f4f6; --ob-bg2:#ffffff; --ob-bg3:#f7f7f8;
  --ob-ink:#0b2a4a; --ob-ink2:#1e416b; --ob-ink3:#41618c;
  --ob-line:#d7dbe2; --ob-line2:#c5ccd6; --ob-hover:rgba(12,127,251,.06);
  --ob-card-shadow:0 18px 50px rgba(24,59,103,.12);
  --ob-mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  --ob-r-sm:8px;
  --ob-danger:#c62828;
}
html[data-theme="dark"] #screen-onboard{
  --ob-bg:#0b1f3a; --ob-bg2:#10294a; --ob-bg3:#173058;
  --ob-ink:#e8edf4; --ob-ink2:#9db0c7; --ob-ink3:#5b7089;
  --ob-line:#1e3a63; --ob-line2:#28466f; --ob-hover:rgba(255,255,255,.05);
  --ob-card-shadow:0 18px 50px rgba(0,0,0,.45);
}
#screen-onboard{padding:0}
#screen-onboard.on{
  position:fixed; inset:0; z-index:50; display:flex; align-items:center; justify-content:center;
  padding:16px; overflow-y:auto;
  background:linear-gradient(to bottom,#ffffff 0,#ffffff 200px,var(--ob-blue) 200px,var(--ob-blue) 224px,var(--ob-bg) 224px);
}
html[data-theme="dark"] #screen-onboard.on{
  background:linear-gradient(to bottom,#10294a 0,#10294a 200px,var(--ob-blue) 200px,var(--ob-blue) 224px,var(--ob-bg) 224px);
}
#screen-onboard .obcard{
  width:100%; max-width:430px; margin:0; text-align:left;
  background:var(--ob-bg2); border:1px solid var(--ob-line); border-radius:22px;
  padding:22px; box-shadow:var(--ob-card-shadow); color:var(--ob-ink);
  font:13px/1.45 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
}
#screen-onboard .hd{display:flex; align-items:center; gap:10px; margin-bottom:4px}
#screen-onboard .pyr{width:44px; height:auto}
#screen-onboard h1{font-size:19px; letter-spacing:.01em; font-weight:700; margin:0; font-family:inherit; color:var(--ob-ink)}
#screen-onboard .tag{font-size:11.5px; color:var(--ob-ink2); margin-bottom:14px}
#screen-onboard .tag b{color:#8d6f00}
html[data-theme="dark"] #screen-onboard .tag b{color:var(--ob-gold)}
#screen-onboard .pill{font-size:9.5px; padding:1px 7px; border-radius:99px; border:1px solid var(--ob-line2); color:var(--ob-ink3); text-transform:uppercase; letter-spacing:.05em; font-weight:400}
#screen-onboard label{display:block; margin:12px 0 4px; font-size:11.5px; color:var(--ob-ink2); font-weight:600; text-align:left}
#screen-onboard label .lbl2{color:var(--ob-ink3); font-weight:400}
#screen-onboard input,#screen-onboard textarea{
  width:100%; font:inherit; color:var(--ob-ink); background:var(--ob-bg3);
  border:1px solid var(--ob-line2); border-radius:var(--ob-r-sm); padding:6px 9px; outline:none;
}
#screen-onboard input:focus,#screen-onboard textarea:focus{border-color:var(--ob-acc)}
#screen-onboard ::placeholder{color:var(--ob-ink3)}
#screen-onboard .obtoggle{display:flex; gap:4px; margin-bottom:12px}
#screen-onboard .obtoggle button{flex:1; font-size:11.5px; padding:6px; border-radius:var(--ob-r-sm); border:1px solid var(--ob-line2); color:var(--ob-ink2); background:none; cursor:pointer}
#screen-onboard .obtoggle button.on{border-color:var(--ob-acc); color:var(--ob-ink); background:var(--ob-hover); font-weight:700}
#screen-onboard .row2{display:flex; gap:8px}
#screen-onboard .row2 .btn,#screen-onboard .row2 input{flex:1; min-width:0}
#screen-onboard .btn{
  background:linear-gradient(135deg,var(--ob-acc),var(--ob-blue-soft)); color:var(--ob-acc-ink);
  font-weight:700; font-size:12.5px; padding:8px 16px; border-radius:10px; border:none; cursor:pointer;
  box-shadow:0 8px 18px rgba(12,127,251,.22);
}
#screen-onboard .btn:disabled{opacity:.45; cursor:default; box-shadow:none}
#screen-onboard .btn.ghost{background:var(--ob-bg3); color:var(--ob-ink); border:1px solid var(--ob-line2); font-weight:600; box-shadow:none}
#screen-onboard .btn.gold{background:var(--ob-gold); color:#231c00; box-shadow:none}
#screen-onboard .btn.wide{width:100%}
#screen-onboard .feenote{font-size:10px; color:var(--ob-ink3); text-align:left; margin-top:0}
#screen-onboard .feenote b{color:var(--ob-ink2)}
#screen-onboard .linklike{display:block; margin:14px auto 0; background:none; border:none; color:var(--ob-blue); font-size:12px; cursor:pointer; text-decoration:underline; text-underline-offset:3px}
#screen-onboard .qrframe{display:flex; justify-content:center; margin:6px 0 2px}
#screen-onboard #obQrBox{width:190px; height:190px; border-radius:12px; background:#fff; border:1px solid var(--ob-line); display:flex; align-items:center; justify-content:center; position:relative}
#screen-onboard #obQrBox .qrph{font-family:var(--ob-mono); font-size:34px; color:var(--ob-line2); letter-spacing:2px}
#screen-onboard #obQrBox svg{width:100%; height:100%; display:block; border-radius:10px}
#screen-onboard .keybox{font-family:var(--ob-mono); font-size:10.5px; background:var(--ob-bg3); border:1px dashed var(--ob-line2); border-radius:var(--ob-r-sm); padding:8px; word-break:break-all; user-select:all; color:var(--ob-ink)}
#screen-onboard .savekey{display:flex; gap:8px; align-items:flex-start; margin-top:9px; font-size:11.5px; color:var(--ob-ink2)}
#screen-onboard .savekey input{width:auto; margin-top:2px}
#screen-onboard .settingsec{border-top:1px solid var(--ob-line); margin-top:12px; padding-top:10px}
#screen-onboard .settingsec h4{font-size:11px; text-transform:uppercase; letter-spacing:.07em; color:var(--ob-ink3); margin-bottom:6px}
#screen-onboard .spinner{display:inline-block; width:13px; height:13px; border:2px solid var(--ob-line2); border-top-color:var(--ob-blue); border-radius:50%; animation:spin .8s linear infinite; vertical-align:-2px; margin-right:4px}
@keyframes spin{to{transform:rotate(360deg)}}

/* The two ways in. Both buttons full width so neither reads as the "small"
   option; the Vault one is ghost so the lead is unambiguous. */
#screen-onboard #obChoose{ display:flex; flex-direction:column; gap:10px; margin:16px 0 4px; }
#screen-onboard #obChoose .btn{ height:46px; font-size:15px; font-weight:700; }
#screen-onboard #obBack{ margin-top:0; margin-bottom:10px; display:inline-block; }

/* ---- password fields: show/hide, and problems stated in place ----
   The eye sits INSIDE the field's box (the input reserves room for it via
   padding-right) so it never reflows the form when it appears. */
.pwwrap{ position:relative; display:block; }
.pwwrap input{ width:100%; padding-right:44px; }
.pweye{
  position:absolute; top:50%; right:6px; transform:translateY(-50%);
  width:32px; height:32px; display:flex; align-items:center; justify-content:center;
  border-radius:8px; color:var(--ob-ink3); cursor:pointer; padding:0;
  background:none; border:none;
}
#screen-onboard .pweye:hover{ color:var(--ob-ink); background:var(--ob-hover); }
.pweye svg{ width:20px; height:20px; display:block; }
.pweye.on{ color:var(--ob-acc); }
/* In Arabic and Hebrew the text starts on the RIGHT, so an eye pinned to the
   right sits on top of it. Mirror the whole arrangement, padding included.
   The onboarding card restyles its inputs behind an ID, which outranks a
   plain class rule - so the room for the eye has to be reserved at the same
   specificity, in both directions. */
html[dir="rtl"] .pwwrap input{ padding-right:12px; padding-left:44px; }
html[dir="rtl"] .pweye{ right:auto; left:6px; }
#screen-onboard .pwwrap input{ padding-right:44px; }
html[dir="rtl"] #screen-onboard .pwwrap input{ padding-right:9px; padding-left:44px; }

/* A mismatch is a STOP, not a whisper: red text under the field, both boxes
   outlined, stated the moment it is true rather than only on submit. */
.fieldErr{
  margin-top:8px; padding:9px 12px; border-radius:9px;
  border:2px solid #c62828; color:#c62828;
  font-size:13.5px; font-weight:700; line-height:1.4;
}
#screen-onboard .fieldErr{ color:#c62828; border-color:#c62828; background:rgba(198,40,40,.07); }
html[data-theme="hc"] #screen-onboard .fieldErr{ color:#8c0000; border-color:#8c0000; background:rgba(140,0,0,.10); }
input.err, .pwwrap input.err{ border-color:#c62828; box-shadow:0 0 0 3px rgba(224,40,40,.18); }
#screen-onboard input.err{ border-color:#c62828; box-shadow:0 0 0 3px rgba(198,40,40,.16); }

/* The account warning, repeated at the moment of no return. Bigger than the
   inline note by design - this is the last point where a forgotten password
   is still fixable. */
.obWarnBig{
  border:2px solid #c62828; border-radius:12px;
  padding:14px 16px; margin:4px 0 6px; font-size:15px; line-height:1.55;
}
.obWarnBig b.lead{ display:block; font-size:16px; margin-bottom:8px; }
.obWarnBig .hl{
  font-weight:800; background:rgba(245,199,17,.24);
  padding:1px 5px; border-radius:5px;
}

/* ---------------------------------------------------------------------
   Settings
   --------------------------------------------------------------------- */

.profBlock { border: 1px solid var(--rule); border-radius: 6px; padding: 0.9rem 1rem; margin-bottom: 1rem; }
.profName { font-family: var(--serif-display); font-size: 1.15rem; color: var(--navy); }

.modeRow { display: flex; gap: 0.3rem; margin-bottom: 1rem; }
/* INK, NEVER MUTED OR ACCENT, ON CONTROLS THAT MUST BE READ. A theme
   picks --muted and --accent for flavour against its own surfaces;
   only --ink is DEFINED by contrast. Muted tab labels on a dark purple
   theme were "practically the same as the theme" (owner screenshot,
   2026-08-12). And every control answers the finger: a pressed chip
   moves. */
.modebtn {
  flex: 1;
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: 0.4rem 0.3rem;
  font-size: 0.78rem;
  cursor: pointer;
  transition: transform 90ms ease-out;
}
.modebtn:active { transform: scale(0.94); }
.modebtn.on {
  color: var(--accent-ink, var(--paper));
  background: var(--accent, var(--navy));
  border-color: transparent;
}

.setPane { display: none; }
.setPane.on { display: block; }

.setRow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--rule);
}

.setRow > span:first-child { flex: 1; }

.setRow select {
  font-family: var(--sans);
  font-size: 0.82rem;
  padding: 0.3rem 0.4rem;
  border: 1px solid var(--rule);
  border-radius: 4px;
  background: transparent;
  color: var(--ink);
}

.setRow input[type='checkbox'] { width: 1.1rem; height: 1.1rem; flex: none; }

.setNote {
  font-size: 0.74rem;
  color: var(--muted);
  line-height: 1.5;
  margin: 0.4rem 0 0.8rem;
}

.verVal { font-size: 0.76rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.acctNote { font-size: 0.8rem; color: var(--muted); margin-bottom: 0.8rem; }

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--paper);
  color: var(--ink);
}

body {
  font-family: var(--serif-body);
  font-size: var(--body-size);
  line-height: var(--leading);
  -webkit-text-size-adjust: 100%;
}

/* ---------------------------------------------------------------------
   The reading surface
   --------------------------------------------------------------------- */

/* Spec 17.5: articles render WITHOUT JavaScript. They are documents served
 * from nodes, and progressive enhancement is the right posture for a platform
 * whose whole claim is durability. Nothing in this block depends on script. */
.article {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 3rem 1.25rem 6rem;
}

.article h1 {
  font-family: var(--serif-display);
  font-size: 2.4em;
  line-height: 1.12;
  letter-spacing: -0.02em;   /* 17.3: tight tracking at large sizes */
  color: var(--navy);
  margin: 0 0 0.4em;
  font-weight: 600;
}

.article h2 {
  font-family: var(--serif-display);
  font-size: 1.45em;
  line-height: 1.22;
  color: var(--navy);
  margin: 2em 0 0.5em;
  font-weight: 600;
}

.article .subtitle {
  font-size: 1.15em;
  color: var(--muted);
  margin: 0 0 1.6em;
  line-height: 1.4;
}

.article p { margin: 0 0 1.35em; }

.article a {
  color: var(--blue);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

.article blockquote {
  margin: 1.6em 0;
  padding-left: 1.2em;
  border-left: 3px solid var(--rule);
  color: var(--muted);
}

.article pre {
  font-family: ui-monospace, 'Cascadia Code', Consolas, monospace;
  font-size: 0.82em;
  line-height: 1.5;
  background: rgba(11, 42, 74, 0.04);
  border: 1px solid var(--rule);
  border-radius: 4px;
  padding: 0.9em 1em;
  /* Wide content scrolls inside its own box; the page never scrolls
   * horizontally. */
  overflow-x: auto;
}

.article img,
.article video { max-width: 100%; height: auto; }

/* Images in a piece. A figure not yet verified holds its place quietly; one
 * that cannot be verified says so in words. Never a broken-image glyph. */
.body-img { margin: 2rem 0; }
.body-img img {
  display: block;
  width: 100%;
  border-radius: 3px;
  background: var(--rule);
  min-height: 6rem;
}
.body-img.loaded img { min-height: 0; background: transparent; }
.body-img figcaption {
  font-family: var(--sans);
  font-size: 0.76rem;
  color: var(--muted);
  margin-top: 0.5rem;
  text-align: center;
}
.body-img figcaption:empty { display: none; }
.body-img.missing {
  border: 1px dashed var(--rule);
  border-radius: 3px;
  padding: 1.2rem;
}
.body-extlink { font-family: var(--sans); font-size: 0.85rem; color: var(--muted); overflow-wrap: anywhere; }

/* ---------------------------------------------------------------------
   Controls
   --------------------------------------------------------------------- */

/* `.btn` is used by the MODALS as well as by the sign-in card. The card has
 * its own scoped copies above, in Chess's palette; these are the app-theme
 * versions for everything else. Both are needed - deleting these left every
 * settings button unstyled, which is how this comment came to exist. */
/* Buttons ride the THEME's design system - accent pill for actions,
 * frost pill for the quiet ones - not the navy slabs the sign-in card
 * brought over from Chess ("quite ugly", owner, 2026-08-12). The card
 * keeps its own scoped copies; everything else matches the dock. */
.btn {
  display: inline-block;
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--accent-ink, var(--paper));
  background: var(--accent, var(--navy));
  border: 1px solid transparent;
  border-radius: var(--r-full, 999px);
  padding: 0.55rem 1.15rem;
  cursor: pointer;
  text-align: center;
  transition: transform 120ms ease-out;
}
.btn:active { transform: scale(0.96); }

.btn.wide { display: block; width: 100%; }
.btn.ghost {
  background: var(--frost, transparent);
  /* INK, not accent: only --ink is DEFINED by contrast against the
     theme's own surfaces. Accent-on-frost was unreadable on the purple
     themes (owner screenshot, 2026-08-12). */
  color: var(--ink, var(--blue));
  border-color: var(--rule);
  font-weight: 500;
}
/* The named PRIMARY of a screen: same accent, floating a little. */
.btn.gold { box-shadow: var(--shadow-float, 0 2px 10px rgba(13, 3, 26, 0.2)); font-weight: 700; }
.btn.sm { font-size: 0.76rem; padding: 0.35rem 0.7rem; }
.btn[disabled] { opacity: 0.5; cursor: default; }
/* The danger variant exists for exactly one class of act: the irreversible.
 * If it appears anywhere reversible, that is a bug (Chess uses it the same
 * way). */
.btn.danger { background: #c62828; }
.btn.danger[disabled] { background: #c62828; }

button,
.button {
  font-family: var(--sans);
  font-size: 0.85rem;
  color: var(--paper);
  background: var(--navy);
  border: 1px solid var(--navy);
  border-radius: 4px;
  padding: 0.5rem 0.95rem;
  cursor: pointer;
}

button.quiet,
.button.quiet {
  background: transparent;
  color: var(--blue);
  border-color: var(--rule);
}

button[disabled] { opacity: 0.5; cursor: default; }

/* 17.5: visible keyboard focus. Uses the bright blue, which is a large
 * non-text indicator and passes the 3:1 threshold that applies to it. */
:focus-visible {
  outline: 3px solid var(--blue-bright);
  outline-offset: 2px;
}

/* ---------------------------------------------------------------------
   Chain-derived values
   --------------------------------------------------------------------- */

/* I-16: the client never decides value. A figure that has not arrived from
 * the chain renders as '-', never as a default or a zero. The dimmed style
 * makes a pending figure visibly pending rather than looking like a real
 * result of nothing. */
.profTop { display: flex; align-items: center; }

#modal textarea {
  width: 100%;
  font-family: var(--sans);
  font-size: 0.88rem;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 0.55rem 0.7rem;
  resize: vertical;
}

/* Embedded attachments in the EDITOR: the picture in the page, atomic and
   quietly furnished. The name rides in CSS content so the figure contributes
   no text to the document model. */
.ed-embed {
  position: relative;
  /* Slim margins: the newline that follows every reference already renders
     one ruled line of air, and 1.4rem on top of that read as an un-deletable
     two-line gap between stacked photos (user screenshot, 2026-08-08).
     snapEmbed still pads the bottom to the line grid. */
  margin: 0.4rem 0;
  max-width: 100%;
}

.ed-embed img,
.ed-embed video {
  /* FULL WIDTH, like the entry will render. The old 320px height cap made
   * every portrait phone photo shrink to half the column and hug the left
   * edge (user screenshot, 2026-08-08) - the editor must show the picture
   * the size the diary will. */
  display: block;
  width: 100%;
  border-radius: 6px;
}

.ed-embed video { background: #000; }
.ed-embed audio { display: block; width: 100%; }

/* An audio embed lays the remove button BESIDE the player, never on top of
   it - the player's own menu lives in its top-right corner, and a floating
   x there stacked on the browser's three-dot button (user screenshot,
   2026-08-08). */
.ed-embed[data-kind="audio"]:not(.missing) {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
.ed-embed[data-kind="audio"]:not(.missing) audio { flex: 1; min-width: 0; }
.ed-embed[data-kind="audio"]:not(.missing) .ed-embed-x {
  position: static;
  flex: none;
}

/* The name label appears ONLY when something needs saying - a missing file,
 * an unplayable format. A healthy photo does not need its filename printed
 * under it. */
.ed-embed.missing::after {
  content: attr(data-name);
  display: block;
  font-family: var(--sans);
  font-size: 0.74rem;
  color: var(--muted);
  margin-top: 0.35rem;
}

.ed-embed.missing {
  border: 1px dashed var(--rule);
  border-radius: 6px;
  padding: 1rem;
}

/* The remove button: a small paper-white circle, visible on touch screens
   (no hover there to reveal it), faded in on hover where a pointer exists. */
.ed-embed-x {
  position: absolute;
  top: 0.45rem;
  right: 0.45rem;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: 50%;
  background: var(--paper);
  box-shadow: 0 1px 4px rgba(11, 42, 74, 0.22);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.ed-embed-x::before {
  content: '\00d7';
  color: var(--ink);
  font-size: 1.05rem;
  line-height: 1;
}

@media (hover: hover) and (pointer: fine) {
  .ed-embed:not([data-kind="audio"]) .ed-embed-x {
    opacity: 0;
    transition: opacity 120ms;
  }
  .ed-embed:hover .ed-embed-x,
  .ed-embed-x:focus-visible { opacity: 1; }
}

/* Video and audio in a body. Same figure furniture as an image so a mixed
   entry reads as one column. */
.body-media { margin: 1.6rem 0; }

/* THE SAME RULE THE POSTER OBEYS, so the two states are the same size and
   pressing play does not resize the entry. width:100% letterboxed a
   portrait clip into black pillars and made the played video WIDER than
   the still that stood in for it - both visible in the same pair of
   screenshots (owner, Android, 2026-08-21). Sized under a ceiling instead,
   the element is the clip and the paper shows around it. */
.body-media video {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 70vh;
  margin: 0 auto;
  border-radius: 6px;
  background: transparent;
}

.body-media audio { display: block; width: 100%; }

.body-missing {
  font-family: var(--sans);
  font-size: 0.82rem;
  color: var(--muted);
  border-left: 3px solid var(--rule);
  padding-left: 0.8rem;
}

/* Forms inside a modal dialog. Bare labels and inputs flow INLINE in HTML,
 * which is what made the give-access dialog look like a ransom note (user
 * screenshot, 2026-08-05). Any dialog form gets the stacked treatment by
 * wrapping its fields in .mform - one class, consistent everywhere. */
.mform label {
  display: block;
  font-family: var(--sans);
  font-size: 0.82rem;
  color: var(--ink);
  margin: 0.95rem 0 0.3rem;
}

.mform input,
.mform textarea {
  display: block;
  width: 100%;
  box-sizing: border-box;
  font-family: var(--sans);
  font-size: 0.92rem;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 0.55rem 0.7rem;
}

.mform .setNote { margin-top: 0.9rem; }

/* The diary. Deliberately quieter than the public surfaces - it is a
 * private room, not a shopfront. Gold marks the locked/unlocked state,
 * which is the one thing a diarist must never misread. */
.diary-gate { max-width: 26rem; }
.diary-gate label { display: block; font-family: var(--sans); font-size: 0.82rem; margin: 0.9rem 0 0.3rem; }
/* Air between the last field and the button - they sat touching. */
.diary-gate .btn { margin-top: 1.2rem; }
.diary-gate .fieldErr { margin: 0.8rem 0 0; }
.diary-gate input {
  width: 100%;
  font-family: var(--sans);
  font-size: 0.9rem;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 0.5rem 0.7rem;
}

/* The account path under the password form - a quiet alternative, ruled
 * off so the two ways in read as two ways in. */
.gate-alt { border-top: 1px solid var(--rule); margin-top: 1.6rem; padding-top: 0.4rem; }
.gate-alt .btn { margin-top: 0.4rem; }

/* The two-kinds-of-guest toggle in the give-access dialog. */
.segrow { display: flex; gap: 0.5rem; margin-bottom: 0.4rem; }
.segrow .btn { flex: 1 1 0; }

.diary-list { border-top: 1px solid var(--rule); margin-top: 1.4rem; }

.diary-row {
  display: flex;
  align-items: baseline;
  gap: 0.8rem;
  border-bottom: 1px solid var(--rule);
  padding: 0.2rem 0;
  flex-wrap: wrap;
}

.diary-open {
  flex: 1;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  background: none;
  border: none;
  text-align: left;
  cursor: pointer;
  padding: 0.85rem 0.25rem;
  color: inherit;
}

.diary-open:hover .diary-title { color: var(--blue); }

.diary-title {
  font-family: var(--serif-display);
  font-size: 1.1rem;
  color: var(--navy);
}

.diary-date { font-size: 0.74rem; color: var(--muted); white-space: nowrap; }

.diary-body {
  flex-basis: 100%;
  padding: 0.2rem 0.25rem 1.1rem;
  font-family: var(--serif-body);
  font-size: var(--body-size);
  line-height: var(--leading);
  border-left: 3px solid var(--gold);
  margin: 0 0 0.6rem 0.25rem;
  padding-left: 0.9rem;
}

.diary-body[hidden] { display: none; }

.settingsec-h {
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 1.4rem 0 0.5rem;
}

/* The waiting state. Motion is the whole point - a static line during a
 * wait is what made the app look stuck - so it is deliberately small and
 * calm, and it respects reduced-motion like everything else. */
.loading {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--sans);
  font-size: 0.85rem;
  color: var(--muted);
  padding: 1.5rem 0;
}

.loading-dots { display: inline-flex; gap: 4px; }

.loading-dots i {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--muted);
  opacity: 0.35;
  animation: loadingPulse 1.1s ease-in-out infinite;
}

.loading-dots i:nth-child(2) { animation-delay: 0.16s; }
.loading-dots i:nth-child(3) { animation-delay: 0.32s; }

@keyframes loadingPulse {
  0%, 80%, 100% { opacity: 0.25; transform: translateY(0); }
  40% { opacity: 1; transform: translateY(-3px); }
}

.figure { font-family: var(--sans); font-variant-numeric: tabular-nums; }
.figure.pending { color: var(--muted); }

/* ---------------------------------------------------------------------
   Editor
   --------------------------------------------------------------------- */

/* Distraction-free by default: a title, the text, and one quiet bar. The
 * formatting tools exist only over a selection. */

.edbar {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.55rem 1.25rem;
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
  font-family: var(--sans);
  font-size: 0.78rem;
  color: var(--muted);
}

.edbar-back {
  color: var(--blue);
  text-decoration: none;
  white-space: nowrap;
}
.edbar-back:hover { text-decoration: underline; }

.edbar-state {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
}

.edbar-dot { width: 3px; height: 3px; border-radius: 50%; background: var(--rule); flex: none; }

.edbar-actions {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  /* WRAP, never overflow. On a phone this row once ran past the screen
   * edge, which does not just clip the Save button - an overflowing row
   * WIDENS THE MOBILE LAYOUT VIEWPORT, and every fixed overlay after it
   * (the pen surface, every dialog) renders against that phantom width and
   * is cut off at the right edge (user screenshots, 2026-08-08). */
  flex-wrap: wrap;
  row-gap: 0.4rem;
}

.edbar-quiet { color: var(--muted); font-size: 0.72rem; }
.edbar-quiet:empty { display: none; }

.editor-wrap {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 2.5rem 1.25rem 8rem;
}

.title-input {
  display: block;
  width: 100%;
  border: none;
  outline: none;
  background: transparent;
  resize: none;
  overflow: hidden;
  padding: 0;
  font-family: var(--serif-display);
  font-size: 2.2em;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--navy);
  margin-bottom: 1.2rem;
}

.title-input::placeholder { color: var(--muted); opacity: 0.55; }
/* Saving without a title lands here: the field itself asks, in the
   accent, and calms down at the first keystroke. */
.title-input.ed-need { border-bottom: 2px solid var(--accent, #b3261e); }
.title-input.ed-need::placeholder { color: var(--accent, #b3261e); opacity: 0.8; }

.editor {
  min-height: 55vh;
  outline: none;
  font-family: var(--serif-body);
  font-size: var(--body-size);
  line-height: var(--leading);
  color: var(--ink);
  /* THE TEXT MODEL: newlines are real characters and must render as such.
   * Without this, a loaded draft's paragraphs collapse into one line. */
  white-space: pre-wrap;
  overflow-wrap: break-word;
  /* THE RULED GUIDE - lined paper, one faint rule per text line. In the
   * gap between a picture and the next writing you can COUNT the blank
   * lines, so you know exactly where the caret will land and whether two
   * images will sit flush; wherever words or a picture live, they simply
   * sit over the lines the way ink sits on a page. Embeds are snapped to
   * this grid by the editor (snapEmbed), so the rules stay true below
   * them.
   *
   * THE LINE BOX IS SPELLED OUT, NOT ASKED FOR. This used to be `1lh`,
   * which Firefox only learned in 120 - so on an older Firefox the whole
   * gradient was invalid and the paper came out blank, no lines to write
   * on at all (owner report, 2026-08-15). The two numbers that MAKE this
   * element's line box are right here as variables (font-size is
   * --body-size, line-height is the unitless --leading), so their
   * product IS one line box on every engine ever built - and it still
   * tracks the reader's text-size setting, because --body-size is rem. */
  --rule-step: calc(var(--body-size) * var(--leading));
  background-image: repeating-linear-gradient(
    to bottom,
    transparent 0,
    transparent calc(var(--rule-step) - 1px),
    color-mix(in srgb, var(--rule) 55%, transparent) calc(var(--rule-step) - 1px),
    color-mix(in srgb, var(--rule) 55%, transparent) var(--rule-step)
  );
}

.editor:empty::before {
  content: attr(data-placeholder);
  color: var(--muted);
  opacity: 0.55;
}

/* The selection toolbar - present only over a selection, floats above it. */
.edtools {
  position: absolute;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 4px;
  background: var(--navy);
  border-radius: 8px;
  box-shadow: 0 6px 24px rgba(11, 42, 74, 0.35);
}

.edtools[hidden] { display: none; }

.edtool {
  font-family: var(--sans);
  font-size: 0.8rem;
  line-height: 1;
  color: var(--paper);
  background: transparent;
  border: none;
  border-radius: 5px;
  padding: 7px 9px;
  cursor: pointer;
  min-width: 30px;
}

.edtool:hover { background: rgba(255, 255, 255, 0.14); }
.edtool b, .edtool i { font-family: var(--serif-body); }

.edtools-rule { width: 1px; align-self: stretch; margin: 4px 3px; background: rgba(255, 255, 255, 0.25); }

/* Preview renders through the article styles - it IS the article. */
.edpreview { padding-top: 0; }
.edpreview[hidden] { display: none; }

/* First-run guidance (12.2): a quiet card, not a tour and not a modal. */
.edguide,
.dguide {
  max-width: var(--measure);
  margin: 1.5rem auto 0;
  padding: 1.1rem 1.25rem;
  border: 1px solid var(--rule);
  border-left: 3px solid var(--gold);
  border-radius: 6px;
  font-family: var(--sans);
  font-size: 0.85rem;
  line-height: 1.55;
  color: var(--ink);
}

.edguide h2,
.dguide h2 {
  font-family: var(--serif-display);
  font-size: 1.15rem;
  color: var(--navy);
  margin: 0 0 0.6rem;
}

.edguide p,
.dguide p { margin: 0 0 0.6rem; }
.edguide b,
.dguide b { color: var(--navy); }

/* The diary's copy of the guide sits inside .article, which already has its
 * own measure - so it fills that rather than imposing a second one. */
.dguide { max-width: none; margin: 1.2rem 0 0; }

/* ---------------------------------------------------------------------
   The diary's header row and list actions
   --------------------------------------------------------------------- */

.desk-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 2.2rem;
}

.desk-head h1 {
  font-family: var(--serif-display);
  font-size: 2rem;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--navy);
  margin: 0 0 0.3rem;
}

.desk-id {
  font-family: var(--sans);
  font-size: 0.8rem;
  color: var(--muted);
  margin: 0;
}

.desk-row-act {
  flex: none;
  font-family: var(--sans);
  font-size: 0.72rem;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: 4px;
  padding: 0.25rem 0.55rem;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease;
}

/* The reveal is bound to the DIARY row - a quiet action that appears when
 * the row is hovered or focused, and is simply always there on touch. */
.desk-row-act:focus-visible { opacity: 1; }
.diary-row:hover .desk-row-act,
.diary-row:focus-within .desk-row-act { opacity: 1; }

.desk-row-act:hover { color: var(--ink); border-color: var(--muted); }

/* Touch screens have no hover: the action is simply always there. */
@media (hover: none) {
  .desk-row-act { opacity: 1; }
}

.desk-empty {
  font-family: var(--sans);
  font-size: 0.85rem;
  color: var(--muted);
  padding: 0.85rem 0.25rem;
  border-bottom: 1px solid var(--rule);
  margin: 0;
}

.desk-empty a { color: var(--blue); }

@media (max-width: 34rem) {
  .desk-head { flex-direction: column; align-items: flex-start; }
}

/* ---------------------------------------------------------------------
   Quality floor
   --------------------------------------------------------------------- */

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

@media (max-width: 34rem) {
  .article,
  .editor-wrap { padding-left: 1rem; padding-right: 1rem; }
  .article h1 { font-size: 1.9em; }
  .edbar { flex-wrap: wrap; gap: 0.5rem; padding: 0.5rem 1rem; }
  .edbar-actions { gap: 0.4rem; }
  .drawbar { gap: 0.45rem; padding: 0.5rem 0.75rem; }
  .draw-title { display: none; }
  .draw-acts { margin-left: 0; width: 100%; justify-content: flex-end; }
  .edbar-state { order: 3; flex-basis: 100%; }
}

/* ---------------------------------------------------------------------
   Paper-diary import: the page and its words, side by side
   --------------------------------------------------------------------- */

.desk-acts { display: flex; gap: 0.5rem; align-items: center; }

.dimp {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 4fr);
  gap: 1.25rem;
  margin: 1rem 0;
}
.dimp-img {
  margin: 0;
  border: 1px solid var(--rule);
  border-radius: 8px;
  overflow: hidden;
  background: var(--paper);
  align-self: start;
}
.dimp-img img { display: block; width: 100%; height: auto; }
.dimp-side { display: flex; flex-direction: column; gap: 0.75rem; min-width: 0; }
.dimp-text {
  min-height: 16rem;
  resize: vertical;
  font: inherit;
  color: var(--ink);
  padding: 0.75rem;
  border: 1px solid var(--rule);
  border-radius: 8px;
  background: var(--paper);
}
.dimp-acts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: space-between;
  align-items: center;
}
.dimp-nav { display: flex; gap: 0.5rem; align-items: center; }
/* The two ways to get words for a page - stacked with air, the AI lead
   first. They rendered touching because this class had no rule at all
   (owner screenshot, 2026-08-12). */
.dimp-choose {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  margin-bottom: 0.9rem;
}
.dimp-count {
  font-family: var(--sans);
  font-size: 0.78rem;
  color: var(--muted, #777);
  min-width: 3em;
  text-align: center;
}
/* Removing a page is quiet but consequential - the red says which. */
.dimp-remove { color: #c62828; }
/* The shooting desk's pager and remove: CENTRED rows. space-between
   wraps into left-ragged lines on a phone (owner screenshot,
   2026-08-12) - a wrapped row must centre, not lean. */
.dimp-snap-nav { margin-top: 0.75rem; justify-content: center; column-gap: 1.4rem; }
@media (max-width: 480px) {
  .dimp-acts { justify-content: center; }
}
.dimp-done { display: flex; justify-content: flex-end; margin: 1rem 0 2rem; }
.dimp-again { display: flex; gap: 0.5rem; align-items: center; }

.dimp-last { max-width: 28rem; margin: 1rem auto; }
.dimp-snap-acts { justify-content: space-between; flex-wrap: wrap; gap: 0.6rem; }
/* On a phone the pair stacks; stretched pills read as a proper action
   sheet instead of two ragged chips. */
.dimp-snap-acts .btn { flex: 1 1 14rem; }

@media (max-width: 700px) {
  .dimp { grid-template-columns: 1fr; }
}

/* ---------------------------------------------------------------------
   The audience squares - who can read this entry
   --------------------------------------------------------------------- */

/* One square per guest, ON THE PAGE above the title - who an entry is for
 * is part of the entry, not a toolbar control (user mockup, 2026-08-08).
 * Selected reads as a pressed button (filled, initial in paper);
 * deselected hollows out and dims. The three slot colours are fixed so
 * "the green one is Mum" stays true across sessions. */
.ed-audience {
  display: flex;
  gap: 6px;
  align-items: center;
  margin: 0.2rem 0 0.6rem;
}
.ed-audience:empty { display: none; }

.aud-chip {
  width: 32px;
  height: 32px;
  padding: 0;
  border-radius: 8px;
  font-family: var(--sans);
  font-size: 0.8rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  border: 2px solid transparent;
  color: var(--paper);
  opacity: 1;
}

.aud-c0 { background: var(--navy); border-color: var(--navy); }
.aud-c1 { background: #0b6ad4; border-color: #0b6ad4; }
.aud-c2 { background: #2e7d32; border-color: #2e7d32; }

.aud-chip:not(.on) {
  background: transparent;
  opacity: 0.55;
}
.aud-c0:not(.on) { color: var(--navy); }
.aud-c1:not(.on) { color: #0b6ad4; }
.aud-c2:not(.on) { color: #2e7d32; }

/* The entry picker inside "Give someone access": what an already-written
   diary shares, each row holdable-back before the key exists.

   THESE ROWS MUST ESCAPE .mform's stacked-field treatment. The picker sits
   inside .mform, whose `label {display:block}` and `input {display:block;
   width:100%; border...}` outspecify a bare .pickrow - the row collapsed
   into a centered checkbox, a run-together date+title, and a full-width
   bordered strip that WAS the stretched checkbox (user screenshot,
   2026-08-08). Every rule here carries the .mform prefix so it wins. */
.picklist {
  max-height: 40vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  margin: 0.6rem 0;
}

.mform label.pickrow {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin: 0;
  padding: 0.3rem 0.1rem;
  font-size: 0.9rem;
  cursor: pointer;
}

.mform .pickrow input {
  display: inline-block;
  flex: none;
  width: 1.1rem;
  height: 1.1rem;
  margin: 0;
  padding: 0;
  border: none;
  accent-color: var(--navy);
}

.pickdate { color: var(--muted); font-size: 0.76rem; white-space: nowrap; }
.picktitle { overflow-wrap: anywhere; }

/* ---------------------------------------------------------------------
   The background picker
   --------------------------------------------------------------------- */

.bggrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(5.4rem, 1fr));
  gap: 0.5rem;
  margin-top: 0.6rem;
}

.bgtile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  padding: 0.5rem 0.3rem 0.45rem;
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: 8px;
  cursor: pointer;
}

.bgtile.on { border-color: var(--navy); box-shadow: 0 0 0 1px var(--navy); }

.bgtile-swatch {
  display: block;
  width: 100%;
  height: 3.2rem;
  border-radius: 5px;
  background-color: var(--paper);
  border: 1px solid var(--rule);
}

.bgtile-label { font-family: var(--sans); font-size: 0.68rem; }

.bgswatches {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  align-items: center;
}

.bgswatch {
  width: 26px;
  height: 26px;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 50%;
  cursor: pointer;
}

.bgswatch.on { border-color: var(--ink); box-shadow: 0 0 0 2px var(--paper) inset; }

.bgcustom {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-family: var(--sans);
  font-size: 0.74rem;
  color: var(--muted);
  cursor: pointer;
}
.bgcustom input { width: 26px; height: 26px; padding: 0; border: 1px solid var(--rule); border-radius: 6px; background: transparent; cursor: pointer; }

.bgslider { display: flex; align-items: center; gap: 0.8rem; }
.bgslider input[type="range"] { flex: 1; }
.bgslider .setNote { margin: 0; white-space: nowrap; }

/* ---------------------------------------------------------------------
   The pen - a full-screen drawing surface
   --------------------------------------------------------------------- */

.drawwrap {
  position: fixed;
  inset: 0;
  z-index: 80;
  background: var(--paper);
  display: flex;
  flex-direction: column;
}

.drawbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  padding: 0.55rem 1rem;
  border-bottom: 1px solid var(--rule);
  font-family: var(--sans);
  background: var(--paper);
}

.draw-title {
  font-family: var(--serif-display);
  font-size: 1.1rem;
  color: var(--navy);
  margin-right: 0.4rem;
}

.draw-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  flex: 1;
  min-width: 0;
}

.draw-rule { width: 1px; height: 22px; background: var(--rule); margin: 0 4px; }

.pen-swatch {
  width: 24px;
  height: 24px;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 50%;
  cursor: pointer;
}
.pen-swatch.on { border-color: var(--paper); box-shadow: 0 0 0 2px var(--ink); }

.pen-size {
  width: 30px;
  height: 30px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: 6px;
  cursor: pointer;
}
.pen-size i { display: block; border-radius: 50%; background: var(--ink); }
.pen-size.on { border-color: var(--navy); box-shadow: 0 0 0 1px var(--navy); }

#drawEraser[aria-pressed="true"] { background: var(--navy); color: var(--paper); }

.draw-acts { display: flex; gap: 0.5rem; margin-left: auto; }

.drawpad {
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px;
}

/* THE PAPER wears the theme and carries the EDITOR'S OWN ruled guide -
   same declaration, same font metrics, so 1lh here is 1lh there and the
   handwriting lands on the diary's lines. The rules are a guide, never
   content: they live under a transparent canvas and no pixel of them
   reaches the entry. */
.drawpaper {
  position: relative;
  background-color: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 6px;
  box-shadow: 0 6px 24px rgba(11, 42, 74, 0.08);
  max-width: 100%;
  max-height: 100%;
  font-family: var(--serif-body);
  font-size: var(--body-size);
  line-height: var(--leading);
  /* Spelled out for the same reason as the editor's - see --rule-step
     there. draw.js measures the real line box off this element, so the
     ink and the rules stay on one grid whatever the engine supports. */
  --rule-step: calc(var(--body-size) * var(--leading));
  background-image: repeating-linear-gradient(
    to bottom,
    transparent 0,
    transparent calc(var(--rule-step) - 1px),
    color-mix(in srgb, var(--rule) 55%, transparent) calc(var(--rule-step) - 1px),
    color-mix(in srgb, var(--rule) 55%, transparent) var(--rule-step)
  );
}
.drawpaper.no-rules { background-image: none; }

#drawCanvas {
  /* touch-action none is what lets a finger DRAW instead of scrolling the
   * page out from under the line. The canvas is TRANSPARENT - only ink -
   * so the themed paper beneath shows through and the export carries
   * alpha. */
  touch-action: none;
  position: absolute;
  inset: 0;
  cursor: crosshair;
}

/* The Auto swatch: an A in the theme's own ink on its own paper - the
   swatch IS the preview of the result. */
.pen-swatch.pen-auto {
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--rule);
  font-family: var(--serif-display);
  font-weight: 700;
  font-size: 0.85rem;
  line-height: 1;
}

/* The eraser's reach, visible. */
.draw-ecur {
  position: absolute;
  border: 1.5px solid color-mix(in srgb, var(--ink) 55%, transparent);
  border-radius: 50%;
  pointer-events: none;
}

/* An auto drawing: an alpha mask filled with the CURRENT text colour -
   in the editor, the reader, everywhere - so the handwriting follows the
   theme forever. */
.draw-auto {
  display: block;
  background: currentColor;
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}
/* A DRAWING SITS ON THE PAGE - no frame, no card, no shadow, and above
   all no loading plate behind it: ink was drawn on transparency and must
   land on transparency, in every colour. */
.body-draw { background: none; border: none; box-shadow: none; padding: 0; }
.body-draw img {
  background: transparent;
  min-height: 0;
  border-radius: 0;
  width: auto;
  max-width: 100%;
}

/* ---------------------------------------------------------------------
   The voice recorder
   --------------------------------------------------------------------- */

.recwrap {
  position: fixed;
  inset: 0;
  z-index: 85;
  background: rgba(11, 42, 74, 0.45);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 1rem;
  /* A CARD TALLER THAN THE SCREEN MUST STILL REACH ITS BUTTONS. With a
     viewfinder, a clip and three actions the card outgrew the phone, and
     "Add to entry" sat below the edge with nothing willing to scroll
     (owner screenshot, 2026-08-15). */
  overflow-y: auto;
  overscroll-behavior: contain;
}

.reccard {
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: 8px;
  width: 100%;
  max-width: 26rem;
  /* Small on purpose: a filming card is tall, and 8vh of air above it
     was 8vh the buttons did not have. */
  margin: 3vh auto;
  padding: 1.25rem;
  font-family: var(--sans);
}

.recmeter {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0.6rem 0;
}

.recdot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #c62828;
  animation: recPulse 1.2s ease-in-out infinite;
}

@keyframes recPulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.45; transform: scale(0.8); }
}

.rectime { font-size: 1.1rem; }

#recPreview audio { display: block; width: 100%; margin: 0.6rem 0; }

/* ---------------------------------------------------------------------
   The image viewer
   --------------------------------------------------------------------- */

/* Any tapped picture, full screen. The dark ground is what makes a photo
 * read at size; the image itself is transformed, never resized, so zooming
 * costs no relayout. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: rgba(9, 14, 22, 0.93);
  display: flex;
  align-items: center;
  justify-content: center;
  touch-action: none;
  overflow: hidden;
}

.lightbox-img {
  max-width: 96vw;
  max-height: 96vh;
  border-radius: 3px;
  transform-origin: center center;
  user-select: none;
  -webkit-user-drag: none;
  cursor: grab;
}

.lightbox-x {
  position: absolute;
  top: max(0.6rem, env(safe-area-inset-top));
  right: 0.6rem;
  z-index: 1;
  width: 2.4rem;
  height: 2.4rem;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(251, 250, 247, 0.14);
  color: #fbfaf7;
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
}
.lightbox-x:hover { background: rgba(251, 250, 247, 0.28); }

/* Pictures in bodies and embeds are now openable - say so with the cursor. */
.body-img img,
.dimp-img img,
.ed-embed img { cursor: zoom-in; }

/* The iOS field floor (zoom-on-focus fix) LIVES AT THE END OF app.css now -
   it must be the last font-size rule in the whole cascade, and app.css is
   the last stylesheet. Do not reintroduce it here. */
