/* ============================================================================
   m3e.css — Material 3 Expressive token + component layer (v01r00)
   Colour roles (--md-sys-color-*) are injected at runtime by m3e.js.
   This file is static (radius/elevation/motion/type/components). Loaded
   site-wide so sticker share pages share the theme; the SPA shell (.app,
   nav bar/rail, views) only appears on the home page.
   ============================================================================ */
@layer reset, tokens, base, components, utilities;

@layer reset {
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
  button, input, select, textarea { font: inherit; color: inherit; }
  :focus-visible { outline: 3px solid var(--md-sys-color-primary); outline-offset: 2px; border-radius: 6px; }
  img, svg { display: block; max-width: 100%; }
}

@layer tokens {
  :root {
    --r-xs: 4px; --r-sm: 8px; --r-md: 12px; --r-lg: 16px; --r-lgi: 20px;
    --r-xl: 28px; --r-xli: 32px; --r-full: 9999px;
    --e1: 0 1px 2px rgb(0 0 0/.30), 0 1px 3px 1px rgb(0 0 0/.15);
    --e2: 0 1px 2px rgb(0 0 0/.30), 0 2px 6px 2px rgb(0 0 0/.15);
    --e3: 0 4px 8px 3px rgb(0 0 0/.15), 0 1px 3px rgb(0 0 0/.30);
    --m-spatial-fast: 350ms cubic-bezier(.42,1.67,.21,.9);
    --m-spatial: 500ms cubic-bezier(.38,1.21,.22,1);
    --m-effects: 200ms cubic-bezier(.34,.8,.34,1);
    --font: "Open Huninn", system-ui, -apple-system, "Noto Sans CJK TC", "Microsoft JhengHei", sans-serif;
  }
  @media (prefers-reduced-motion: reduce) {
    :root { --m-spatial-fast: 1ms linear; --m-spatial: 1ms linear; --m-effects: 1ms linear; }
  }
  /* Explicit Animations control (m3e.js data-reduce, from the motion auto/on/off seg)
     overrides the OS media query: "on" (data-reduce=0) restores spring motion even when
     the OS prefers reduced; "off"/auto-reduced (data-reduce=1) flattens it. */
  html[data-reduce="0"] { --m-spatial-fast: 350ms cubic-bezier(.42,1.67,.21,.9); --m-spatial: 500ms cubic-bezier(.38,1.21,.22,1); --m-effects: 200ms cubic-bezier(.34,.8,.34,1); }
  html[data-reduce="1"] { --m-spatial-fast: 1ms linear; --m-spatial: 1ms linear; --m-effects: 1ms linear; }
}
html[data-reduce="1"] .view { animation: none; }

@layer base {
  html { font-family: var(--font); -webkit-text-size-adjust: 100%; }
  body {
    background: var(--md-sys-color-background, #fff);
    color: var(--md-sys-color-on-surface, #111);
    min-height: 100dvh; line-height: 1.5; overscroll-behavior-y: contain;
    transition: background var(--m-effects), color var(--m-effects);
  }
  a { color: var(--md-sys-color-primary); }
  .t-display { font-size: 2.6rem; line-height: 1.16; font-weight: 400; }
  .t-headline { font-size: 2.8rem; line-height: 1.16; font-weight: 400; }   /* M3E v14 .t-display-m */
  .t-title { font-size: 1.2rem; line-height: 1.3; font-weight: 600; }
  .t-title-s { font-size: .95rem; line-height: 1.4; font-weight: 600; }
  .t-body { font-size: 1rem; line-height: 1.5; font-weight: 400; }
  .t-label { font-size: .8rem; line-height: 1.4; font-weight: 600; letter-spacing: .02em; }
  .muted { color: var(--md-sys-color-on-surface-variant); }
}

@layer components {
  /* ---- app shell (home page only) ---- */
  .app { max-width: 1080px; margin: 0 auto; padding-bottom: 96px; }
  .appbar {
    position: sticky; top: 0; z-index: 30; display: flex; align-items: center; gap: 12px;
    padding: max(env(safe-area-inset-top), 12px) 16px 12px;
    background: var(--md-sys-color-surface-container);
    transition: background var(--m-effects);
  }
  .appbar .brand { display: flex; align-items: center; gap: 10px; margin-right: auto; min-width: 0; }
  .appbar .brand i { color: var(--md-sys-color-primary); font-size: 1.3rem; }
  .appbar .brand h1 { font-size: 1.15rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* v03r43: countdown to the next automatic location fix, shown only while a routine is running on
     THIS device (the brand's margin-right:auto pushes it to the far right). flex-shrink:0 so a long
     region name in the title never squeezes it — the title already ellipsises. */
  .appbar-rt { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px;
    border-radius: var(--r-full); background: var(--md-sys-color-secondary-container);
    color: var(--md-sys-color-on-secondary-container); font-size: .8rem; font-weight: 600; font-variant-numeric: tabular-nums; }
  .appbar-rt i { font-size: .9rem; }

  .appmain { padding: 0 16px; }
  .view { display: none; animation: m3fade var(--m-spatial); }
  .view.active { display: block; }
  @keyframes m3fade { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
  @media (prefers-reduced-motion: reduce) { .view { animation: none; } }

  section.block { margin-top: 22px; }
  .sec-head { display: flex; align-items: center; gap: 10px; margin: 8px 0 14px; }
  .sec-head i { color: var(--md-sys-color-primary); }

  /* r37: collapsible Settings cards — the .sec-head is the clickable toggle */
  .card.collapsible > .sec-head { cursor: pointer; user-select: none; }
  .card.collapsible > .sec-head .sec-caret { margin-left: auto; transition: transform var(--m-effects, .2s); }
  .card.collapsible:not(.collapsed) > .sec-head .sec-caret { transform: rotate(180deg); }
  .card.collapsible.collapsed > .sec-head { margin-bottom: 0; }
  .card.collapsible.collapsed > :not(.sec-head) { display: none; }

  .card {
    background: var(--md-sys-color-surface-container-low);
    border-radius: var(--r-xl); padding: 18px; margin-bottom: 16px;
    transition: background var(--m-effects);
  }
  .panel { background: var(--md-sys-color-surface-container); border-radius: var(--r-xl); padding: 18px; }
  .field { margin-bottom: 20px; }
  .field > label { display: block; font-weight: 600; font-size: .9rem; margin-bottom: 10px; }
  .range { display: flex; align-items: center; gap: 14px; }
  .range input[type=range] { flex: 1; }
  .range output { font-variant-numeric: tabular-nums; font-weight: 600; min-width: 3.4em; text-align: right; color: var(--md-sys-color-on-surface-variant); }
  input[type=range] { accent-color: var(--md-sys-color-primary); cursor: pointer; }
  .hint { color: var(--md-sys-color-on-surface-variant); font-size: .82rem; margin-top: 8px; }
  .kv { display: flex; justify-content: space-between; gap: 12px; padding: 8px 0; }
  hr { border: none; border-top: 1px solid var(--md-sys-color-outline-variant); margin: 14px 0; }

  /* ---- buttons ---- */
  .m3-btn {
    position: relative; display: inline-flex; align-items: center; gap: 8px; border: none;
    cursor: pointer; height: 44px; padding: 0 22px; border-radius: var(--r-full);
    font-weight: 600; font-size: .9rem; overflow: hidden; isolation: isolate;
    transition: border-radius var(--m-spatial), box-shadow var(--m-effects);
  }
  .m3-btn::before { content: ""; position: absolute; inset: 0; background: currentColor; opacity: 0; border-radius: inherit; transition: opacity var(--m-effects); z-index: -1; }
  .m3-btn:hover::before { opacity: .08; }
  .m3-btn:active { border-radius: var(--r-md); }
  .m3-btn:active::before { opacity: .10; }
  .m3-btn.filled { background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary); }
  .m3-btn.tonal { background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container); }
  .m3-btn.outlined { background: transparent; color: var(--md-sys-color-primary); box-shadow: inset 0 0 0 1px var(--md-sys-color-outline); }
  .m3-btn.text { background: transparent; color: var(--md-sys-color-primary); padding: 0 14px; }
  .m3-btn.danger { background: var(--md-sys-color-error-container); color: var(--md-sys-color-on-error-container); --fa-primary-color: var(--fa-error-primary); --fa-secondary-color: var(--fa-error-secondary); }
  .m3-btn[disabled] { opacity: .5; pointer-events: none; }
  .btn-flow { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }

  /* ---- segmented control ---- */
  .seg { display: inline-flex; flex-wrap: wrap; background: var(--md-sys-color-surface-container-highest); border-radius: var(--r-full); padding: 4px; gap: 4px; }
  .seg button {
    border: none; background: none; cursor: pointer; display: inline-flex; align-items: center; gap: 8px;
    padding: 9px 16px; border-radius: var(--r-full); font-weight: 600; font-size: .85rem;
    color: var(--md-sys-color-on-surface-variant); transition: background var(--m-effects), color var(--m-effects);
  }
  .seg button[aria-pressed=true] { background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary); }

  /* ---- chips / swatches ---- */
  .chip-flow { display: flex; gap: 10px; flex-wrap: wrap; }
  .chip {
    display: inline-flex; align-items: center; gap: 8px; height: 36px; padding: 0 14px; cursor: pointer;
    border-radius: var(--r-sm); background: var(--md-sys-color-surface-container);
    color: var(--md-sys-color-on-surface-variant); border: 1px solid var(--md-sys-color-outline-variant);
    font-weight: 600; font-size: .85rem; transition: background var(--m-effects);
  }
  .chip[aria-pressed=true] { background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container); border-color: transparent; }
  .swatch { width: 40px; height: 40px; border-radius: var(--r-full); border: 2px solid transparent; cursor: pointer; box-shadow: var(--e1); transition: transform var(--m-spatial-fast); }
  .swatch:hover { transform: scale(1.08); }
  .swatch[aria-pressed=true] { border-color: var(--md-sys-color-on-surface); }

  /* ---- switch ---- */
  .m3-switch { display: inline-flex; align-items: center; gap: 12px; cursor: pointer; }
  .m3-switch input { position: absolute; opacity: 0; width: 1px; height: 1px; }
  .m3-switch .track { position: relative; width: 52px; height: 32px; border-radius: var(--r-full); background: var(--md-sys-color-surface-container-highest); box-shadow: inset 0 0 0 2px var(--md-sys-color-outline); transition: background var(--m-effects); }
  .m3-switch .track::before { content: ""; position: absolute; top: 8px; left: 8px; width: 16px; height: 16px; border-radius: var(--r-full); background: var(--md-sys-color-outline); transition: transform var(--m-spatial-fast), width var(--m-spatial-fast), height var(--m-spatial-fast), top var(--m-spatial-fast), left var(--m-spatial-fast); }
  .m3-switch input:checked + .track { background: var(--md-sys-color-primary); box-shadow: none; }
  .m3-switch input:checked + .track::before { width: 24px; height: 24px; top: 4px; left: 4px; transform: translateX(20px); background: var(--md-sys-color-on-primary); }

  /* ---- navigation: bottom bar (compact) -> rail (>=600px), per M3E v14 ---- */
  .navbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 40; display: flex; justify-content: space-around;
    padding: 10px 8px max(env(safe-area-inset-bottom), 10px);
    background: var(--md-sys-color-surface-container); transition: background var(--m-effects);
  }
  .navbar button, .navrail button {
    border: none; background: none; cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 4px;
    padding: 4px 6px; color: var(--md-sys-color-on-surface-variant); font-size: .72rem; font-weight: 600;
    transition: color var(--m-effects);
  }
  .navbar .ind { display: flex; align-items: center; justify-content: center; width: 64px; height: 32px; border-radius: var(--r-full); transition: background var(--m-spatial-fast); }
  .navrail .ind { display: flex; align-items: center; justify-content: center; width: 56px; height: 32px; border-radius: var(--r-full); transition: background var(--m-spatial-fast); }
  .navbar button i, .navrail button i { font-size: 1.2rem; }
  .navbar button[aria-pressed=true], .navrail button[aria-pressed=true] { color: var(--md-sys-color-on-secondary-container); }
  .navbar button[aria-pressed=true] .ind, .navrail button[aria-pressed=true] .ind { background: var(--md-sys-color-secondary-container); }
  .navrail { display: none; }
  .rail-menu { margin-bottom: 6px; }
  /* expandable rail (data-rail=expanded): icons-only 80px -> labelled 216px */
  html[data-rail=expanded] { --rail-w: 216px; }
  html[data-rail=expanded] .navrail { align-items: stretch; padding: 72px 12px 16px; gap: 6px; }
  html[data-rail=expanded] .navrail button { flex-direction: row; justify-content: flex-start; gap: 14px; padding: 12px 18px; border-radius: var(--r-full); font-size: .92rem; line-height: 1.2; }
  html[data-rail=expanded] .navrail .ind { width: auto; height: auto; background: none; }
  html[data-rail=expanded] .navrail button[aria-pressed=true] { background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container); }
  @media (min-width: 600px) {
    .navbar { display: none; }
    .navrail {
      position: fixed; left: 0; top: 0; bottom: 0; z-index: 20; width: var(--rail-w, 80px); display: flex; flex-direction: column;
      align-items: center; gap: 14px; padding: 76px 0 16px;
      background: var(--md-sys-color-surface-container); transition: background var(--m-effects), width var(--m-spatial);
    }
    .app { padding-left: var(--rail-w, 80px); padding-bottom: 16px; }
    .posts { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); justify-items: stretch; }
    .sticker-card { max-width: none; }
  }

  /* ---- sticker grid (home view) ---- */
  .intro { text-align: center; padding: 8px 0 4px; }
  .intro img { margin: 0 auto 8px; border-radius: var(--r-lg); max-width: 360px; }
  /* mobile portrait: single column; multi-column only on wider screens (see @media below) */
  .posts { display: grid; grid-template-columns: 1fr; gap: 14px; margin: 14px 0; justify-items: center; }
  .sticker-card {
    width: 100%; max-width: 340px; min-width: 0;
    background: var(--md-sys-color-surface-container-low); border-radius: var(--r-lg); padding: 12px;
    text-align: center; transition: background var(--m-effects), transform var(--m-spatial-fast);
  }
  .sticker-card:active { transform: scale(.97); }
  .sticker-card a { display: block; cursor: pointer; }
  .sticker-card img { width: 100%; height: auto; border-radius: var(--r-md); }
  .sticker-card h2 { font-size: .92rem; font-weight: 600; line-height: 1.35; margin-top: 10px; color: var(--md-sys-color-on-surface); }
  .sticker-card hr { display: none; }
  .credit { color: var(--md-sys-color-on-surface-variant); font-size: .75rem; text-align: center; margin-top: 24px; }
  .credit a { color: var(--md-sys-color-primary); }

  /* text input */
  .m3-input { width: 100%; background: var(--md-sys-color-surface-container-highest); color: var(--md-sys-color-on-surface); border: none; border-bottom: 2px solid var(--md-sys-color-outline); border-radius: var(--r-sm) var(--r-sm) 0 0; padding: 13px 14px; transition: border-color var(--m-effects); }
  .m3-input:focus { outline: none; border-bottom-color: var(--md-sys-color-primary); }
  .kv code { font-variant-numeric: tabular-nums; color: var(--md-sys-color-on-surface-variant); }

  /* toast (inverse surface for contrast on any scheme) */
  .toast { position: fixed; left: 50%; bottom: 88px; transform: translateX(-50%) translateY(20px); z-index: 60; max-width: 88vw; padding: 12px 18px; border-radius: var(--r-sm); background: var(--md-sys-color-on-surface); color: var(--md-sys-color-surface); box-shadow: var(--e3); opacity: 0; pointer-events: none; transition: opacity var(--m-effects), transform var(--m-spatial-fast); font-size: .9rem; }
  .toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
  @media (min-width: 600px) { .toast { bottom: 24px; } }

  /* ---- map view (region preview + list) ---- */
  .region-current { display: inline-flex; align-items: center; gap: 8px; font-weight: 600; color: var(--md-sys-color-on-surface); }
  .region-current i { color: var(--md-sys-color-primary); }
  .cu-map { height: 340px; margin-top: 14px; border-radius: var(--r-lg); overflow: hidden; background: var(--md-sys-color-surface-container-highest); }
  .region-list { display: flex; flex-direction: column; gap: 8px; }
  .region-row { display: flex; align-items: center; gap: 8px; }
  .region-open {
    flex: 1; min-width: 0; display: flex; align-items: center; gap: 12px; cursor: pointer; border: none; text-align: left;
    background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface);
    border-radius: var(--r-md); padding: 12px 14px; transition: background var(--m-effects);
  }
  .region-open:hover { background: var(--md-sys-color-surface-container-highest); }
  .region-open i { color: var(--md-sys-color-primary); }
  .region-open .region-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .region-open .region-meta { margin-left: auto; font-size: .75rem; color: var(--md-sys-color-on-surface-variant); white-space: nowrap; }

  /* ---- small-screen polygon editor (full-screen overlay) ---- */
  /* z-index must clear Leaflet's internal panes/controls (up to ~1000), or the preview
     map's tiles/controls paint through the overlay (the "double map" bug). */
  .region-editor { position: fixed; inset: 0; z-index: 1200; display: flex; flex-direction: column; background: var(--md-sys-color-surface); }
  /* belt-and-suspenders: stop rendering the preview map entirely while editing */
  body.editor-open .cu-map { visibility: hidden; }
  .region-editor .editor-bar {
    display: flex; align-items: center; gap: 12px; padding: max(env(safe-area-inset-top), 12px) 14px 12px;
    background: var(--md-sys-color-surface-container);
  }
  .region-editor .editor-bar h2 { margin-right: auto; }
  .cu-edit-map { flex: 1; min-height: 0; background: var(--md-sys-color-surface-container-highest); }
  .region-editor .editor-form { padding: 14px 16px max(env(safe-area-inset-bottom), 16px); display: flex; flex-direction: column; gap: 12px; background: var(--md-sys-color-surface-container); }
  .editor-line { display: flex; align-items: center; gap: 10px; }
  .editor-line .hint { margin-top: 0; flex: 1; }
  .vtx-count { font-weight: 600; font-size: .82rem; color: var(--md-sys-color-on-surface-variant); white-space: nowrap; }
  body.editor-open { overflow: hidden; }
  .vtx-tip { background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary); border: none; border-radius: var(--r-full); padding: 0 6px; font-size: .7rem; font-weight: 700; box-shadow: var(--e1); }
  .vtx-tip::before { display: none; }

  /* ---- following status (Me view) ---- */
  .status-line { display: flex; align-items: center; gap: 10px; font-size: 1.25rem; font-weight: 600; color: var(--md-sys-color-on-surface); }
  .status-line i { color: var(--md-sys-color-primary); }
  .ann-row { padding: 8px 0; border-bottom: 1px solid var(--md-sys-color-outline-variant); }
  .ann-row:last-child { border-bottom: none; }
  .hist-row { display: flex; justify-content: space-between; gap: 12px; padding: 8px 0; border-bottom: 1px solid var(--md-sys-color-outline-variant); }
  .hist-row:last-child { border-bottom: none; }
  .chip-inline { display: inline-block; padding: 1px 8px; border-radius: var(--r-full); background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container); font-size: .78rem; font-weight: 600; }

  /* ---- version line (settings footer) ---- */
  .app-version { text-align: center; margin-top: 6px; color: var(--md-sys-color-on-surface-variant); font-size: .72rem; font-variant-numeric: tabular-nums; letter-spacing: .02em; }

  /* ---- update bar (shown when the service worker finds a new version) ---- */
  .update-bar {
    position: fixed; left: 50%; transform: translateX(-50%) translateY(140%); z-index: 1300;
    bottom: max(env(safe-area-inset-bottom), 12px); width: min(560px, calc(100vw - 24px));
    display: flex; align-items: center; gap: 12px; padding: 12px 14px 12px 18px; border-radius: var(--r-lg);
    background: var(--md-sys-color-primary-container); color: var(--md-sys-color-on-primary-container);
    box-shadow: var(--e3); transition: transform var(--m-spatial); font-size: .9rem;
  }
  .update-bar.show { transform: translateX(-50%) translateY(0); }
  .update-bar span { flex: 1; font-weight: 600; }
  .update-bar button {
    border: none; cursor: pointer; flex: none; height: 38px; padding: 0 18px; border-radius: var(--r-full);
    font-weight: 700; font-size: .85rem; background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary);
  }

  /* ---- device-binding (sync) image ---- */
  .bind-out { margin-top: 14px; text-align: center; }
  .bind-out img { width: 220px; max-width: 70%; margin: 0 auto 10px; image-rendering: pixelated; border-radius: var(--r-md); background: #fff; padding: 8px; }
  /* A Beam frame is rendered at close to 1:1 and read off this screen by another device's camera,
     so it is wider than the TXP/1 image and must not be smoothed. */
  .bind-out canvas { width: 320px; max-width: 92%; height: auto; margin: 0 auto 10px; display: block; image-rendering: pixelated; border-radius: var(--r-md); background: #fff; padding: 8px; }
  .bind-out video { width: 320px; max-width: 92%; margin: 0 auto 10px; display: block; border-radius: var(--r-md); background: #000; }
  .bind-out .seg { margin: 0 auto 10px; }
  .bind-out .hidden { display: none; }
}

@layer utilities {
  .icon-btn {
    width: 44px; height: 44px; border-radius: var(--r-full); border: none; cursor: pointer;
    background: transparent; color: var(--md-sys-color-on-surface-variant); font-size: 1.1rem;
    display: inline-flex; align-items: center; justify-content: center; position: relative; overflow: hidden;
  }
  .icon-btn::before { content: ""; position: absolute; inset: 0; background: currentColor; opacity: 0; border-radius: inherit; transition: opacity var(--m-effects); }
  .icon-btn:hover::before { opacity: .08; }
  .hidden { display: none !important; }
}

/* ---- theme fine-tune controls (v01r22): native colour pickers, HSL sliders,
   hex inputs, gradient direction. Unlayered so they need no @layer juggling. */
.glass-defs { position: absolute; width: 0; height: 0; overflow: hidden; pointer-events: none; }
.seed-row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.seed-pick { position: relative; width: 56px; height: 56px; border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--e1); cursor: pointer; border: none; padding: 0; }
.seed-pick input { position: absolute; inset: -20%; width: 140%; height: 140%; border: none; cursor: pointer; background: none; }
.seed-hex { font-variant-numeric: tabular-nums; font-weight: 600; letter-spacing: .04em; color: var(--md-sys-color-on-surface-variant); }
.tune-btn { width: 44px; height: 44px; flex: none; color: var(--md-sys-color-on-surface-variant); }
.tune { margin-top: 14px; padding: 14px; border-radius: var(--r-lg); background: var(--md-sys-color-surface-container-high); display: flex; flex-direction: column; gap: 10px; }
.tune-row { display: flex; align-items: center; gap: 12px; margin: 0; }
.tune-row > span { width: 1.2em; font-weight: 600; font-size: .8rem; color: var(--md-sys-color-on-surface-variant); }
.tune-row input[type=range] { flex: 1; width: auto; max-width: none; }
.tune-row output { font-variant-numeric: tabular-nums; font-weight: 600; min-width: 2.6em; text-align: right; font-size: .8rem; color: var(--md-sys-color-on-surface-variant); }
.hex-in { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; text-transform: uppercase; letter-spacing: .06em; }
.range .ax { width: 1.2em; font-weight: 600; font-size: .8rem; color: var(--md-sys-color-on-surface-variant); }
.sub-label { display: block; font-size: .75rem; font-weight: 600; margin: 14px 0 4px; color: var(--md-sys-color-on-surface-variant); letter-spacing: .04em; }
.dev-list { margin-top: 8px; }
.dev-row { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--md-sys-color-outline-variant); }
.dev-row:last-child { border-bottom: none; }
.dev-row code { font-variant-numeric: tabular-nums; }
/* single sticker share page (post.html): no navrail, so drop the rail gutter;
   give the lone sticker a touch more presence and space the page title. */
.post-title { margin: 6px 0 4px; }
.sticker-card.single { max-width: 420px; }
@media (min-width: 600px) { .app.solo { padding-left: 0; } }
/* Compact width: segmented buttons (icon + label) show only the icon when not
   selected, so multi-option segs (style, scheme, motion) stay on one row. Bare-text
   segs like language have no <span>, so they're unaffected. */
@media (max-width: 520px) {
  .seg button:not([aria-pressed="true"]) span { display: none; }
}
/* Persistent backend-mismatch warning banner (r21): non-dismissable; shown when the
   user approved a backend whose identity fingerprint differs from the expected one. */
.fp-warn { display: flex; align-items: center; gap: 10px; margin: 10px 0 4px; padding: 12px 16px; border-radius: var(--r-lg); background: var(--md-sys-color-error-container); color: var(--md-sys-color-on-error-container); font-size: .85rem; font-weight: 600; }
.fp-warn i { font-size: 1.1rem; }
.follow-stale { margin-top: 10px; }
.follow-stale .fp-warn { margin: 0 0 8px; }
/* follower cards (r24): one row per tracked person — duotone transport glyph + username,
   with up/down reorder buttons when following more than one. */
.follow-row { padding: 12px 0; border-top: 1px solid var(--md-sys-color-outline-variant); }
.follow-row:first-child { padding-top: 0; border-top: none; }
.follow-row .sec-head { margin: 0 0 8px; }
.follow-glyph { display: inline-flex; font-size: 1.5rem; line-height: 1; }
.follow-reorder { margin-left: auto; display: inline-flex; gap: 4px; }
.follow-reorder .icon-btn[disabled] { opacity: .35; pointer-events: none; }
.follow-status { margin-left: 2px; }
.follow-sticker { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
.follow-sticker img { width: 44px; height: 44px; object-fit: contain; border-radius: 8px; background: var(--md-sys-color-surface-container-high); }
.follow-sticker-t { font-size: .9rem; line-height: 1.35; }
/* follower status card on a per-sticker share page (r28) */
.follow-card { margin-top: 16px; text-align: left; }
.follow-reminder { margin-top: 10px; }
/* home controls row (r31): column toggle on the left, category filter chips fill the rest */
.home-controls { display: flex; align-items: flex-start; gap: 8px; margin: 4px 0 16px; flex-wrap: nowrap; }
.home-controls .sticker-filter { margin: 0; flex: 1 1 auto; }
/* compact (<600): the filter drops to its own second row, separate from the column toggle (r34) */
@media (max-width: 599.98px) {
  .home-controls { flex-direction: column; align-items: flex-start; }
  .home-controls .sticker-filter { width: 100%; }
}
.col-toggle { flex: 0 0 auto; display: inline-flex; border: 1px solid var(--md-sys-color-outline-variant); border-radius: 999px; overflow: hidden; }
.col-toggle button { font: inherit; font-size: .9rem; cursor: pointer; padding: 6px 12px; border: 0; background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface); }
.col-toggle button + button { border-left: 1px solid var(--md-sys-color-outline-variant); }
.col-toggle button[aria-pressed="true"] { background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container); }
/* home sticker category filter (r27) */
.sticker-filter { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 16px; }
.sticker-filter button { font: inherit; font-size: .9rem; cursor: pointer; padding: 6px 14px; border-radius: 999px; border: 1px solid var(--md-sys-color-outline-variant); background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface); }
.sticker-filter button[aria-pressed="true"] { background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container); border-color: transparent; }
/* home grid column choice (r31; 1–3 columns r34): the toggle is authoritative across widths —
   app.js writes the effective count to html[data-cols] from the window size class + the user's
   choice. Higher specificity than the responsive .posts rule above, so it overrides the auto-fill
   multi-column on wide screens too. */
html[data-cols="1"] .posts { grid-template-columns: 1fr; justify-items: center; }
/* minmax(0,1fr): equal tracks that can shrink BELOW their content's min-width, so a card never
   pushes past the viewport (and the left/right columns stay the same size). */
html[data-cols="2"] .posts { grid-template-columns: repeat(2, minmax(0, 1fr)); justify-items: stretch; }
html[data-cols="3"] .posts { grid-template-columns: repeat(3, minmax(0, 1fr)); justify-items: stretch; }
html:not([data-cols="1"]) .sticker-card { max-width: none; }
/* category dividers (r32): a full-width line between sticker categories (it spans every column via
   grid-column 1/-1, so it works in every column count). In single column it tracks the centred card
   width; in multi-column it stretches the full grid. */
.posts .cat-divider { grid-column: 1 / -1; justify-self: center; width: 100%; max-width: 340px; height: 0; border: 0; border-top: 1px solid var(--md-sys-color-outline-variant); margin: 0 auto; }
html:not([data-cols="1"]) .posts .cat-divider { max-width: none; justify-self: stretch; }
/* per-sticker glyph row (r27): icon left, emoji/zh/en right */
.sticker-meta { display: flex; align-items: center; gap: 12px; text-align: left; margin-top: 12px; }
.sticker-ico { flex: 0 0 auto; font-size: 1.6rem; line-height: 1; }
/* centre: emoji + bilingual text. Each <br>-separated line stays on ONE line (nowrap); anything
   too long is clipped rather than wrapping or widening the card (r34). */
.sticker-meta h2 { margin: 0; flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; }
/* right: the share icon (r33). The image now updates only; this anchor triggers the web-share. */
.sticker-meta .sticker-share { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; padding: 6px; margin: -6px -2px -6px 0; font-size: 1.4rem; line-height: 1; color: var(--md-sys-color-primary); text-decoration: none; cursor: pointer; }
.sticker-meta .sticker-share:active { transform: scale(.9); }
/* compact + two columns: the cards are narrow, so shrink the meta row (smaller font + tighter
   icons/gap) to keep the nowrap h2 readable AND the card within the viewport at the same time. */
@media (max-width: 599.98px) {
  html[data-cols="2"] .sticker-meta { gap: 8px; }
  html[data-cols="2"] .sticker-meta h2 { font-size: .7rem; line-height: 1.25; }
  html[data-cols="2"] .sticker-ico { font-size: 1.15rem; }
  html[data-cols="2"] .sticker-meta .sticker-share { font-size: 1.05rem; padding: 4px; margin: -4px -2px -4px 0; }
}
.follow-note { margin: 2px 0 0; }
/* host's follower rows (block / cancel) */
.follower-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; padding: 8px 0; border-top: 1px solid var(--md-sys-color-outline-variant); }
.follower-row:first-of-type { border-top: none; }
.follower-id { display: inline-flex; align-items: center; gap: 8px; }
.follower-actions { display: inline-flex; gap: 4px; margin-left: auto; }
/* identity confirmation (r34) */
.follower-row .sec-head { margin: 0; }
.follower-reply { flex-basis: 100%; font-size: 1.3rem; }
.confirm-reply { display: inline-flex; align-items: center; gap: 8px; }
.confirm-panel { width: 100%; padding: 4px 0 10px; border-bottom: 1px solid var(--md-sys-color-outline-variant); margin-bottom: 10px; }
.confirm-cats { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0; }
/* follower-side prompt lives inside a .follow-row (the following cards) */
.confirm-prompt { margin-top: 10px; padding: 10px 12px; border-radius: var(--r-md); background: var(--md-sys-color-surface-container-high); }
.confirm-prompt.answered { background: transparent; padding: 6px 0 0; }
.confirm-opts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.confirm-opts .m3-btn { font-size: 1.3rem; padding: 8px 14px; }

/* mis-tap management (r30) */
.mistap-group { padding: 12px 0; border-top: 1px solid var(--md-sys-color-outline-variant); }
.mistap-group:first-child { border-top: none; padding-top: 4px; }
.mistap-ref { display: flex; align-items: center; gap: 6px; opacity: .85; margin: 6px 0; }
.mistap-item { display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%; text-align: left;
  margin: 6px 0; padding: 8px 12px; border-radius: var(--r-md); cursor: pointer; font: inherit;
  border: 1px solid var(--md-sys-color-outline-variant); background: var(--md-sys-color-surface-container); color: inherit; }
.mistap-item-main { display: inline-flex; align-items: center; gap: 8px; }
.mistap-item.is-mistap { border-color: var(--md-sys-color-error); }
.mistap-item.is-mistap .mistap-item-main { text-decoration: line-through; opacity: .55; }
.mistap-item.is-mistap .mistap-state { background: var(--md-sys-color-error-container); color: var(--md-sys-color-on-error-container); }
.review-item.is-region { display: flex; align-items: center; gap: 10px; width: 100%; margin: 6px 0; padding: 8px 12px;
  border-radius: var(--r-md); border: 1px dashed var(--md-sys-color-outline-variant); background: transparent; opacity: .7; }

/* ============================ v02: couple Timeline ============================ */
/* The title is one word in either language and must read as one: at 2.8rem 「時間軸」
   broke into 時間/軸 on a phone and the two toggles fell underneath it (2026-08-15).
   Nothing in this row wraps — when the width runs out the toggles drop their labels
   and keep their icons, the same trade the segmented buttons make at line ~330. */
.tl-top-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: nowrap; }
.tl-top-row .t-headline { white-space: nowrap; }
/* The feed is its own scroll container (the IntersectionObserver root); it sits between the appbar
   and the docked event bar. dvh keeps it stable across mobile URL-bar resize. */
.timeline-feed { display: flex; flex-direction: column; gap: 14px; overflow-y: auto; -webkit-overflow-scrolling: touch;
  height: calc(100dvh - 230px); padding: 4px 2px 8px; scroll-padding-bottom: calc(80px + env(safe-area-inset-bottom, 0px)); }
@media (min-width: 600px) { .timeline-feed { height: calc(100dvh - 210px); } }
/* The docked event bar (position:fixed) overlays the feed's bottom in compact view, so the inner
   card block reserves room beneath the last card to scroll it clear of the bar. Reset at >=600px,
   where the bar docks over the rail and no longer overlaps the feed. */
.timeline-feed #tlCards { display: flex; flex-direction: column; gap: 14px; padding-bottom: calc(80px + env(safe-area-inset-bottom, 0px)); }
.tl-sentinel { height: 1px; }
.tl-empty { text-align: center; padding: 32px 8px; }
/* Each card: the OWNER colour (per-user, from the ledger members map) tints the border + connector.
   self = right-aligned, partner = left-aligned. content-visibility keeps offscreen cards cheap. */
.tl-card { position: relative; max-width: 84%; background: var(--md-sys-color-surface-container-low);
  border: 2px solid var(--owner, var(--md-sys-color-primary)); border-radius: var(--r-lg); padding: 12px 14px;
  content-visibility: auto; contain-intrinsic-size: 0 120px; }
.tl-card.self { align-self: flex-end; }
.tl-card.partner { align-self: flex-start; }
.tl-card.dup { border-style: dashed; }   /* a duplicate event (revealed from a collapsed block) */
.tl-connector { position: absolute; top: -14px; width: 3px; height: 16px; background: var(--owner, var(--md-sys-color-primary)); border-radius: 2px; }
.tl-card.self .tl-connector { right: 24px; }
.tl-card.partner .tl-connector { left: 24px; }
.tl-card:first-child .tl-connector { display: none; }
.tl-card.tl-flash, .follow-row.tl-flash { animation: tlFlash 2.2s var(--m-effects, ease); }
@keyframes tlFlash { 0%,100% { box-shadow: none; } 25% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--owner, var(--md-sys-color-primary)) 40%, transparent); } }
.tl-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 6px; }
.tl-glyph { font-size: 1.3rem; color: var(--owner, var(--md-sys-color-primary)); }
/* a sticker event's name, centred in the head between the glyph and the share button */
.tl-head-title { flex: 1; min-width: 0; text-align: center; font-weight: 600; color: var(--md-sys-color-on-surface);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-share { width: 36px; height: 36px; }
.tl-text { margin: 0; white-space: pre-wrap; word-break: break-word; }
/* the sticker image is the card's focal content: large + centred (name is in the head). */
.tl-sticker { display: flex; justify-content: center; align-items: center; }
.tl-sticker img { width: 100%; max-width: 340px; max-height: 340px; height: auto; object-fit: contain; border-radius: var(--r-md); }
.tl-sticker-empty { padding: 16px; font-size: 2.2rem; color: var(--md-sys-color-on-surface-variant); }
.tl-loc { display: flex; align-items: center; gap: 8px; font-weight: 600; }
.tl-legend { margin-top: 8px; }
/* the two header toggles (list-view + map-view) sit together at the right of the timeline top row */
.tl-top-actions { display: flex; align-items: center; gap: 4px; flex-wrap: nowrap; justify-content: flex-end; }
/* Compact width: icon only. The label is the button's accessible name, so both carry
   data-i18n-aria — hiding the <span> without it would leave an unnamed button. */
@media (max-width: 599.98px) {
  .tl-top-actions .m3-btn span { display: none; }
}
/* ---- list layout (compact thumbnail + stacked meta rows; toggled by #btnTlListView) ---- */
.tl-card.list { max-width: 96%; contain-intrinsic-size: 0 96px; }
.tl-list-row { display: flex; align-items: flex-start; gap: 12px; }
.tl-list-thumb { flex: 0 0 auto; width: 72px; height: 72px; display: flex; align-items: center; justify-content: center;
  border-radius: var(--r-md); overflow: hidden; background: var(--md-sys-color-surface-container-high); }
.tl-list-thumb img { width: 100%; height: 100%; object-fit: cover; }
.tl-list-ico { font-size: 1.9rem; color: var(--owner, var(--md-sys-color-primary)); }
.tl-list-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.tl-list-head { display: flex; align-items: center; gap: 8px; }
.tl-list-head .tl-head-title { text-align: left; }   /* left-aligned in list mode (centred in album) */
.tl-list-sub { display: flex; align-items: center; gap: 6px; min-width: 0; font-size: .85rem; color: var(--md-sys-color-on-surface-variant); }
.tl-list-sub > span, .tl-list-sub .tl-region { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Collapsed duplicate block: a one-line summary standing in for a run of repeated same-content
   events (the first, canonical, event is shown as a normal card above it). Tap to expand/collapse. */
.tl-dup { display: inline-flex; align-items: center; gap: 8px; width: auto; max-width: 84%; padding: 6px 14px; cursor: pointer;
  border: 1.5px dashed var(--owner, var(--md-sys-color-outline-variant)); border-radius: var(--r-full);
  background: transparent; color: var(--md-sys-color-primary); font: inherit; font-size: .85rem; font-weight: 600; }
.tl-dup.self { align-self: flex-end; }
.tl-dup.partner { align-self: flex-start; }
.tl-dup:hover, .tl-dup:focus-visible { background: var(--md-sys-color-surface-container); }
.tl-dup .tl-dup-main { white-space: nowrap; }
.tl-dup .tl-dup-chevron { transition: transform .15s var(--m-effects, ease); }
.tl-dup[aria-expanded=true] .tl-dup-chevron { transform: rotate(180deg); }
/* Hidden×N pill: reuses .tl-dup but in a muted tint (vs the primary-tinted Duplicated×N). */
.tl-dup.tl-hidden { color: var(--md-sys-color-on-surface-variant); }
/* Per-card "Wrong?" hide toggle: a small, muted link at the bottom-right of every event card. */
.tl-hide-row { display: flex; justify-content: flex-end; align-items: center; gap: 14px; margin-top: 6px; }
.tl-hide-toggle { background: none; border: 0; padding: 2px 4px; cursor: pointer; font: inherit; font-size: .75rem;
  color: var(--md-sys-color-on-surface-variant); opacity: .8; }
.tl-hide-toggle:hover, .tl-hide-toggle:focus-visible { opacity: 1; color: var(--md-sys-color-primary); text-decoration: underline; }
.tl-card.hid { border-style: dashed; opacity: .9; }   /* a hidden event revealed from a Hidden×N block */
/* Docked event bar (above the navbar; navbar is z-40 fixed-bottom) + the sticker picker above it. */
.timeline-bar { position: fixed; left: 0; right: 0; bottom: var(--tl-dock, calc(72px + env(safe-area-inset-bottom, 0px))); z-index: 39;
  display: flex; align-items: center; gap: 8px; padding: 8px 12px max(env(safe-area-inset-bottom, 0px), 8px);
  background: var(--md-sys-color-surface-container); }
.timeline-bar .m3-input { flex: 1; }
.timeline-bar .tl-send { background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary); }
/* v03r43 (David, 2026-08-15): the feed navigation column, in the gutter beside the cards — up,
   down, jump to the bottom. Fixed, so it holds still while the feed scrolls under it, on the same
   dock offset as the "+" menu so it can never collide with the event bar. Below the bar (39) and
   the "+" menu (40) in the stack. Cards are max-width:84%, so on a compact screen it sits clear of
   one's own (right-aligned) cards; over a partner's it reads as a floating control, which is why it
   carries a surface and a shadow rather than being transparent. */
.tl-nav { position: fixed; left: 8px; bottom: var(--tl-dock2, calc(128px + env(safe-area-inset-bottom, 0px))); z-index: 38;
  display: flex; flex-direction: column; gap: 6px; }
.tl-nav .icon-btn { background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface); box-shadow: var(--e1); }
@media (min-width: 600px) { .tl-nav { left: calc(var(--rail-w, 80px) + 8px); bottom: 72px; } }
.tl-picker { position: fixed; left: 0; right: 0; bottom: var(--tl-dock2, calc(128px + env(safe-area-inset-bottom, 0px))); z-index: 39;
  max-height: 40vh; overflow-y: auto; padding: 10px 12px; background: var(--md-sys-color-surface-container-high);
  border-top: 1px solid var(--md-sys-color-outline-variant); }
.tl-picker-list { display: flex; flex-direction: column; gap: 10px; }
/* event-bar "+" menu (Location / Sticker / Routine) — a compact popup above the bar, left-anchored */
.tl-add-menu { position: fixed; left: 12px; bottom: var(--tl-dock2, calc(128px + env(safe-area-inset-bottom, 0px))); z-index: 40;
  display: flex; flex-direction: column; gap: 2px; padding: 6px; min-width: 190px;
  background: var(--md-sys-color-surface-container-high); border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--r-md); box-shadow: 0 4px 16px rgba(0, 0, 0, .28); }
.tl-add-item { display: flex; align-items: center; gap: 10px; padding: 11px 12px; border: none; background: transparent;
  border-radius: var(--r-sm); font: inherit; font-size: .9rem; font-weight: 600; color: var(--md-sys-color-on-surface); cursor: pointer; text-align: left; }
.tl-add-item:hover, .tl-add-item:active { background: var(--md-sys-color-surface-container-highest); }
@media (min-width: 600px) { .tl-add-menu { left: calc(var(--rail-w, 80px) + 12px); bottom: 72px; } }
.tl-pick { flex: 0 0 auto; width: 56px; border: none; background: transparent; padding: 4px; cursor: pointer; border-radius: var(--r-sm); }
.tl-pick img { width: 100%; height: auto; display: block; }
/* picker tabs (C.U. / Custom) — segmented row at the top of the menu */
.tl-pick-tabs { display: flex; gap: 6px; }
.tl-pick-tab { flex: 1; border: none; cursor: pointer; padding: 8px 10px; border-radius: var(--r-full); font: inherit; font-size: .85rem; font-weight: 600;
  background: var(--md-sys-color-surface-container-highest); color: var(--md-sys-color-on-surface-variant); transition: background var(--m-effects), color var(--m-effects); }
.tl-pick-tab[aria-pressed=true] { background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container); }
@media (min-width: 600px) {
  .timeline-bar { left: var(--rail-w, 80px); bottom: 8px; }
  .tl-picker { left: var(--rail-w, 80px); bottom: 72px; }
  .timeline-feed { scroll-padding-bottom: 24px; }
  .timeline-feed #tlCards { padding-bottom: 0; }
}

/* r04: C.U. tab — one category per horizontally-scrolling row, with a small category label */
.tl-pick-cat { display: flex; flex-direction: column; gap: 4px; }
.tl-pick-cat-label { font-size: .72rem; font-weight: 700; color: var(--md-sys-color-on-surface-variant); padding-left: 2px; }
.tl-pick-row { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 2px; -webkit-overflow-scrolling: touch; }
.tl-pick-custom { background: var(--md-sys-color-surface-container-highest); }
.tl-pick i { font-size: 28px; color: var(--md-sys-color-on-surface-variant); }
/* r04: Custom tab — add button + the pack grid (pick to send, or delete) */
.tl-pick-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 2px; }
.tl-pick-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(72px, 1fr)); gap: 10px; }
.tl-pick-custom-item { position: relative; }
.tl-pick-custom-item .tl-pick { width: 100%; padding: 0; }
.tl-pick-custom-item .tl-pick img { aspect-ratio: 1; object-fit: contain; border-radius: var(--r-sm); }
.tl-pick-custom-item .tl-pick i { display: flex; align-items: center; justify-content: center; aspect-ratio: 1; }
.tl-pack-del { position: absolute; top: 2px; right: 2px; width: 28px; height: 28px; background: rgba(0,0,0,.45); color: #fff; border-radius: 50%; }
.tl-pack-rename { position: absolute; top: 2px; left: 2px; width: 28px; height: 28px; background: rgba(0,0,0,.45); color: #fff; border-radius: 50%; }
.tl-pack-move { position: absolute; bottom: 2px; right: 2px; width: 28px; height: 28px; background: rgba(0,0,0,.45); color: #fff; border-radius: 50%; }
/* custom-sticker collections: a labelled section per collection + the move-mode banner */
.tl-pick-collection { display: flex; flex-direction: column; gap: 6px; }
.tl-pick-coll-head { display: flex; align-items: center; gap: 6px; min-height: 30px; }
.tl-pick-coll-name { flex: 1; min-width: 0; font-size: .72rem; font-weight: 700; color: var(--md-sys-color-on-surface-variant);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-transform: uppercase; letter-spacing: .03em; }
.tl-pick-coll-actions { display: flex; align-items: center; gap: 2px; }
.tl-pick-coll-actions .icon-btn { width: 30px; height: 30px; }
.tl-coll-empty { font-size: .8rem; margin: 0 0 2px; }
.tl-move-banner { display: flex; align-items: center; gap: 8px; padding: 6px 10px; border-radius: var(--r-md); font-size: .8rem;
  background: var(--md-sys-color-tertiary-container); color: var(--md-sys-color-on-tertiary-container); }
.tl-move-banner span { flex: 1; }
/* the video frame picker */
.tl-frame-picker { position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,.6); padding: 16px; }
.tl-frame-box { width: 100%; max-width: 460px; max-height: 92vh; overflow-y: auto; padding: 16px;
  background: var(--md-sys-color-surface-container-high); border-radius: var(--r-lg); display: flex; flex-direction: column; gap: 10px; }
.tl-frame-head { display: flex; align-items: center; justify-content: space-between; }
.tl-frame-video { width: 100%; max-height: 46vh; background: #000; border-radius: var(--r-sm); }
.tl-frame-seek { width: 100%; }
.tl-frame-ctrls { display: flex; align-items: center; justify-content: center; gap: 12px; }
.tl-frame-strip { display: flex; flex-wrap: wrap; gap: 8px; }
.tl-frame-thumb { position: relative; width: 64px; height: 64px; border-radius: var(--r-sm); overflow: hidden; background: var(--md-sys-color-surface-container-highest); }
.tl-frame-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.tl-frame-rm { position: absolute; top: 1px; right: 1px; width: 22px; height: 22px; background: rgba(0,0,0,.5); color: #fff; border-radius: 50%; }

/* v03r33: the photo-annotation editor — same modal shell as the frame picker, a wider box because the
   preview canvas and the per-item style controls sit side by side on a phone in landscape. */
.tl-photoant { position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,.6); padding: 16px; }
.tl-pa-box { width: 100%; max-width: 540px; max-height: 92vh; overflow-y: auto; padding: 16px;
  background: var(--md-sys-color-surface-container-high); border-radius: var(--r-lg); display: flex; flex-direction: column; gap: 10px; }
/* The title + preview STICK to the top of the scrolling panel. The control stack is far taller than
   the dialog, so without this the preview scrolls away and every control below it is adjusted blind —
   which is the whole point of having a preview. Keeping the head inside the sticky block also keeps
   the close button reachable from anywhere in the scroll. */
.pa-top { position: sticky; top: 0; z-index: 2; padding-bottom: 8px;
  background: var(--md-sys-color-surface-container-high); box-shadow: 0 6px 8px -6px rgba(0,0,0,.35); }
.pa-stage { position: relative; display: flex; align-items: center; justify-content: center; min-height: 160px;
  background: var(--md-sys-color-surface-container); border-radius: var(--r-sm); overflow: hidden; }
.pa-stage.hot { outline: 2px dashed var(--md-sys-color-primary); outline-offset: -4px; }
/* the preview never dictates the export size — it is a scaled view of a full-resolution composite.
   32vh (not 44) so the sticky block leaves room for the controls it sits above. */
.pa-canvas { max-width: 100%; max-height: 32vh; display: block; }
.pa-drop { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 24px 16px; text-align: center; }
.pa-drop-ico { font-size: 28px; color: var(--md-sys-color-primary); }
.pa-drop-t { font-size: .95rem; font-weight: 600; }
.pa-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.pa-chip { display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border: none; border-radius: var(--r-full);
  background: var(--md-sys-color-surface-container-highest); color: var(--md-sys-color-on-surface); font: inherit; font-size: .85rem; cursor: pointer; }
.pa-chip[aria-pressed=true] { background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary); }
.pa-adds { display: flex; flex-wrap: wrap; gap: 4px; width: 100%; }
.pa-edit { display: flex; flex-direction: column; gap: 8px; padding-top: 6px; border-top: 1px solid var(--md-sys-color-outline-variant); }
.pa-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.pa-row-l { flex: 0 0 100%; font-size: .8rem; color: var(--md-sys-color-on-surface-variant); }
.pa-seg { font-size: .82rem; }
.pa-seg button:disabled { opacity: .38; cursor: not-allowed; }
.pa-coord { display: inline-flex; align-items: center; gap: 4px; font-variant-numeric: tabular-nums; }
.pa-sel { padding: 5px 8px; border-radius: var(--r-sm); border: 1px solid var(--md-sys-color-outline-variant);
  background: var(--md-sys-color-surface-container-highest); color: var(--md-sys-color-on-surface); font: inherit; font-size: .82rem; }
/* a square is draggable on the preview; snapping is handled in app.js, this is just the affordance */
.pa-canvas { touch-action: none; cursor: grab; }
.pa-canvas:active { cursor: grabbing; }
.pa-colour { width: 40px; height: 32px; padding: 0; border: none; background: none; }
.pa-colour:disabled { opacity: .38; }
.pa-pick { max-width: 100%; }
.pa-prog { display: flex; align-items: center; gap: 8px; }
.pa-prog-track { flex: 1 1 auto; height: 6px; border-radius: var(--r-full); overflow: hidden;
  background: var(--md-sys-color-surface-container-highest); }
.pa-prog-bar { height: 100%; width: 0; border-radius: var(--r-full);
  background: var(--md-sys-color-primary); transition: width .18s ease; }
.pa-prog-label { flex: 0 0 auto; font-size: .78rem; color: var(--md-sys-color-on-surface-variant); }

/* r02: comments (.tl-reply, tinted by the commenter's owner colour) + the insert-between affordance */
.tl-replies { margin-top: 10px; display: flex; flex-direction: column; gap: 6px; }
.tl-reply { display: flex; align-items: center; gap: 6px; padding: 4px 8px; border-left: 3px solid var(--owner, var(--md-sys-color-primary));
  background: var(--md-sys-color-surface-container); border-radius: var(--r-sm); font-size: .9rem; }
.tl-reply.self { flex-direction: row-reverse; border-left: none; border-right: 3px solid var(--owner, var(--md-sys-color-primary)); }
.tl-reply-t { flex: 1; white-space: pre-wrap; word-break: break-word; }
.tl-reply-del { width: 26px; height: 26px; color: var(--md-sys-color-error); }
.tl-reply-form { display: flex; align-items: center; gap: 4px; }
.tl-reply-in { flex: 1; min-width: 0; padding: 5px 10px; border: 1px solid var(--md-sys-color-outline-variant); border-radius: var(--r-md);
  background: var(--md-sys-color-surface); color: var(--md-sys-color-on-surface); font: inherit; font-size: .9rem; }
.tl-reply-send { width: 30px; height: 30px; color: var(--md-sys-color-primary); }
/* Insert "+" between two cards: a hairline rule with a centred button that grows on hover/focus. */
.tl-insert-zone { display: flex; align-items: center; justify-content: center; height: 14px; margin: -4px 0; }
.tl-insert { width: 24px; height: 24px; line-height: 24px; padding: 0; border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: 50%; background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface-variant);
  opacity: .35; cursor: pointer; font-size: .7rem; transition: opacity .12s var(--m-effects, ease), transform .12s var(--m-effects, ease); }
.tl-insert:hover, .tl-insert:focus-visible { opacity: 1; transform: scale(1.15); color: var(--md-sys-color-primary); }
/* Inserting-mode chip docked just above the event bar. */
.tl-insert-chip { position: fixed; left: 0; right: 0; bottom: var(--tl-dock2, calc(128px + env(safe-area-inset-bottom, 0px))); z-index: 39;
  display: flex; align-items: center; gap: 8px; padding: 6px 14px; font-size: .85rem;
  background: var(--md-sys-color-tertiary-container); color: var(--md-sys-color-on-tertiary-container); }
.tl-insert-chip span { flex: 1; }
@media (min-width: 600px) {
  .tl-insert-chip { left: var(--rail-w, 80px); bottom: 64px; }
}

/* ---- v02r06: timeline maps (region-name chip, per-event popup, trip map view) ---- */
.tl-region { display: inline; padding: 0; border: none; background: none; font: inherit; cursor: pointer;
  color: var(--md-sys-color-primary); text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 2px; }
.tl-region .tl-region-ico { font-size: .85em; text-decoration: none; }
.tl-region:hover { color: var(--md-sys-color-on-surface); }
/* Map view (replaces the feed): controls + a full map filling the same area as the feed. */
.tl-map-view { display: flex; flex-direction: column; gap: 10px; height: calc(100dvh - 230px); padding: 4px 2px 8px; }
@media (min-width: 600px) { .tl-map-view { height: calc(100dvh - 210px); } }
.tl-map-controls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.tl-map-range { display: flex; align-items: center; gap: 8px; font-size: .85rem; color: var(--md-sys-color-on-surface-variant); flex: 1; }
.tl-map-range input[type=range] { flex: 1; min-width: 80px; }
.tl-map-range output { min-width: 2ch; text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
.tl-trip-map { flex: 1; min-height: 240px; border-radius: var(--r-lg); overflow: hidden; background: var(--md-sys-color-surface-container); }
/* Per-event map popup (region-name click). */
.tl-map-modal { position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,.6); padding: 16px; }
.tl-map-box { width: 100%; max-width: 520px; max-height: 88vh; display: flex; flex-direction: column; gap: 10px; padding: 14px;
  background: var(--md-sys-color-surface-container-high); border-radius: var(--r-lg); }
.tl-map-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.tl-event-map { width: 100%; height: 58vh; border-radius: var(--r-md); overflow: hidden; background: var(--md-sys-color-surface-container); }
/* numbered trip-map markers (permanent tooltip) — ORDER marker mode */
.leaflet-tooltip.tl-trip-num { background: transparent; border: none; box-shadow: none; color: #fff; font-weight: 700; font-size: 11px; padding: 0; }
.leaflet-tooltip.tl-trip-num::before { display: none; }
/* r19: CATEGORY marker mode — a speech-bubble div-icon holding the location/commute glyph, tinted by
   the category colour (--c). The bottom-left pointed corner is the tail pointing at the location. */
.leaflet-div-icon.tl-trip-bubble { background: transparent; border: none; }
.tl-bubble { display: flex; align-items: center; justify-content: center; width: 30px; height: 30px;
  border-radius: 50% 50% 50% 3px; background: #fff; border: 2px solid var(--c, #2563eb); box-shadow: 0 1px 4px rgba(0,0,0,.4);
  font-size: 15px; color: var(--c, #2563eb); --fa-primary-color: var(--c, #2563eb); --fa-secondary-color: var(--c, #2563eb); --fa-secondary-opacity: .35; }
.tl-bubble i { line-height: 1; }
/* r19: compact range/marker mode toggles inside the map-controls row */
.tl-map-seg { padding: 3px; }
.tl-map-seg button { padding: 6px 12px; font-size: .8rem; }

/* ---- r03 backend validation report (#validateCard / #validateBody) ---- */
.validate-summary { display: flex; align-items: center; gap: 8px; margin: 10px 0 4px; font-weight: 700; font-size: .9rem; }
.validate-summary.ok { color: var(--md-sys-color-primary); }
.validate-summary.fail { color: var(--md-sys-color-error); }
.validate-err { color: var(--md-sys-color-error); font-weight: 600; }
.validate-group { margin-top: 10px; border-top: 1px solid var(--md-sys-color-outline-variant); padding-top: 8px; }
.validate-group-head { display: flex; align-items: center; gap: 6px; font-size: .85rem; }
.validate-group-head.ok { color: var(--md-sys-color-primary); }
.validate-group-head.fail { color: var(--md-sys-color-error); }
.validate-row { display: flex; align-items: baseline; gap: 6px; font-size: .8rem; padding: 2px 0 2px 20px; }
.validate-row.ok { color: var(--md-sys-color-on-surface); }
.validate-row.fail { color: var(--md-sys-color-error); font-weight: 600; }
.validate-name { font-variant-numeric: tabular-nums; }
.validate-muted { color: var(--md-sys-color-on-surface-variant); font-weight: 400; font-size: .76rem; }

/* r11: 貼圖分享對話框（locate → region → 記錄/分享）。沿用 .tl-map-modal 的 modal 模式。 */
.share-dialog { position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,.6); padding: 16px; }
.share-dialog-box { width: 100%; max-width: 420px; display: flex; flex-direction: column; gap: 14px; padding: 18px; background: var(--md-sys-color-surface-container-high); border-radius: var(--r-lg); }
.share-dialog-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.share-dialog-body { min-height: 28px; }
.share-dialog-body .dlg-status { display: flex; align-items: center; gap: 10px; font-size: .95rem; color: var(--md-sys-color-on-surface); }
.share-dialog-actions { display: flex; flex-direction: column; gap: 10px; }
.share-dialog-actions .m3-btn { width: 100%; justify-content: center; }
/* sticker pager: 4-button row (Prev · Cancel · Submit · Next); single-page flows hide all but Done */
.rt-pick-actions { flex-direction: row; flex-wrap: wrap; }
.rt-pick-actions .m3-btn { width: auto; flex: 1 1 0; min-width: 0; padding-left: 10px; padding-right: 10px; }
.rt-pick-actions .m3-btn:disabled { opacity: .38; pointer-events: none; }
.rt-pick-actions .m3-btn span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* v03r31: the routine trip-map toggles — four independent sliders above the single trip-map button.
   Each row shows what it controls plus the option currently selected, so an on/off track is never
   ambiguous about which of the two named options it is on. */
.rt-trip-opts { display: flex; flex-direction: column; gap: 6px; padding: 10px 12px; border-radius: var(--r-md); background: var(--md-sys-color-surface-container); }
.rt-trip-sw { gap: 10px; }
.rt-trip-sw .track { flex: 0 0 auto; }
.rt-sw-txt { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; font-size: .85rem; color: var(--md-sys-color-on-surface-variant); }
.rt-sw-txt b { font-weight: 600; color: var(--md-sys-color-on-surface); }
.share-dialog-actions .dlg-reminder { margin: 0 0 2px; }
.share-dialog-actions .dlg-optin { display: flex; align-items: flex-start; gap: 8px; font-size: .85rem; line-height: 1.3; color: var(--md-sys-color-on-surface-variant); cursor: pointer; margin: 0 0 2px; }
.share-dialog-actions .dlg-optin input { margin-top: 2px; flex: 0 0 auto; accent-color: var(--md-sys-color-primary); }
.dlg-spinner { width: 18px; height: 18px; border-radius: 50%; border: 2px solid var(--md-sys-color-outline-variant); border-top-color: var(--md-sys-color-primary); animation: dlg-spin .8s linear infinite; flex: 0 0 auto; }
@keyframes dlg-spin { to { transform: rotate(360deg); } }
html[data-reduce="1"] .dlg-spinner { animation: none; }
/* deep-link popup (waiting to jump / target not synced yet) */
.deeplink-pop { position: fixed; left: 50%; top: 28%; transform: translateX(-50%); z-index: 70; max-width: 84vw;
  display: flex; align-items: center; gap: 10px; padding: 14px 18px; font-size: .9rem;
  background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface);
  border: 1px solid var(--md-sys-color-outline-variant); border-radius: var(--r-lg); box-shadow: 0 6px 24px rgba(0, 0, 0, .3); }

/* ---- routine (v03 Phase 1): live commute session ---- */
.routine-card .routine-badge { margin-left: auto; font-size: .7rem; font-weight: 700; padding: 2px 8px; border-radius: var(--r-full); background: var(--md-sys-color-surface-container-highest); color: var(--md-sys-color-on-surface-variant); }
.routine-badge.active { background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary); }
.routine-badge.suspended { background: var(--md-sys-color-error-container); color: var(--md-sys-color-on-error-container); }
.routine-route { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-weight: 600; margin: 6px 0; }
.routine-metrics { display: flex; flex-wrap: wrap; gap: 6px 14px; font-size: .85rem; color: var(--md-sys-color-on-surface-variant); margin-bottom: 8px; }
.routine-metrics span { display: inline-flex; align-items: center; gap: 5px; }
.routine-controls { margin: 8px 0; }
.routine-passbys { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }
.routine-passby { display: flex; align-items: center; gap: 8px; padding: 6px 10px; border-radius: var(--r-md); background: var(--md-sys-color-surface-container-high); border-left: 3px solid var(--c, var(--md-sys-color-primary)); }
.routine-passby-name { font-weight: 600; font-size: .85rem; display: inline-flex; align-items: center; gap: 5px; }
.routine-passby-stk { margin-left: auto; display: inline-flex; gap: 4px; color: var(--c, var(--md-sys-color-primary)); }
.routine-suspends { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; font-size: .82rem; color: var(--md-sys-color-on-surface-variant); }
.routine-suspend-row { display: flex; align-items: center; gap: 6px; }
/* chronological journey (region visits + suspend/resume, each with battery); collapsible in list view */
.routine-jtoggle { display: flex; align-items: center; gap: 6px; width: 100%; margin-top: 8px; padding: 6px 10px; border: none; border-radius: var(--r-md); background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface); font: inherit; font-size: .82rem; font-weight: 600; cursor: pointer; }
.routine-jtoggle .routine-jcaret { margin-left: auto; }
.routine-journey { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; }
.routine-jrow { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 8px; padding: 6px 10px; border-radius: var(--r-md); background: var(--md-sys-color-surface-container-high); border-left: 3px solid var(--c, var(--md-sys-color-primary)); font-size: .84rem; }
.routine-jrow.is-event { background: var(--md-sys-color-surface-container); border-left-color: var(--md-sys-color-outline); color: var(--md-sys-color-on-surface-variant); }
/* The name NEVER wraps (David, 2026-08-17). flex:0 0 auto keeps the name column at its own content
   width instead of letting it be squeezed, max-width:100% stops it pushing past the card edge, and
   overflow:hidden clips rather than wraps. That alone gives the first two rungs of the ladder: the
   name shares line 1 with the meta run while both fit, and otherwise the meta run wraps to line 2
   with the name intact on line 1. Rung three (scrolling a name too wide even for a full line) needs
   measurement — fitJourneyRow() in timeline.js. */
.routine-jrow-main { font-weight: 600; display: inline-flex; align-items: center; gap: 5px; flex: 0 0 auto; min-width: 0; max-width: 100%; white-space: nowrap; overflow: hidden; }
.routine-jname { flex: 0 0 auto; white-space: nowrap; }
.routine-jrow.is-event .routine-jrow-main { font-weight: 500; }
/* Everything that is not the name rides in ONE wrapper so it wraps as a BLOCK: time, battery, 🏁,
   sticker chips, ❌ and ✎ move to line 2 together or not at all. As separate row children they wrapped
   independently, which put the buttons on line 2 while the clock stayed on line 1 — a third layout
   nobody asked for. */
.routine-jrow-side { margin-left: auto; display: inline-flex; align-items: center; gap: 4px 8px; flex: 0 0 auto; white-space: nowrap; }
.routine-jrow.is-2line .routine-jrow-side { margin-left: 0; }
.routine-jrow-meta { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; color: var(--md-sys-color-on-surface-variant); font-size: .78rem; }
.routine-jrow-stk { display: inline-flex; gap: 4px; color: var(--c, var(--md-sys-color-primary)); }
.routine-jbatt { display: inline-flex; align-items: center; gap: 3px; }
.routine-jtime { font-variant-numeric: tabular-nums; }
/* ❌ wrong-geolocation annotation (GPS error): struck + dimmed row; toggle tints error when on */
.routine-jrow.is-wronggeo { border-left-color: var(--md-sys-color-error); }
.routine-jrow.is-wronggeo .routine-jrow-main { text-decoration: line-through; opacity: .62; }
.rt-wronggeo.on { color: var(--md-sys-color-error); --fa-primary-color: var(--md-sys-color-error); --fa-secondary-color: var(--md-sys-color-error); }
/* 🏁 真正結束 — the marked row, the excluded tail behind it, and the cut divider */
.routine-jrow.is-realend { border-left-color: var(--md-sys-color-tertiary); }
.routine-jrow.is-afterend { opacity: .5; }
.routine-jrow.is-afterend .routine-jrow-main { text-decoration: line-through; }
/* SUPERSEDED 2026-08-17 (David) — a rule here used to force every action row onto ONE line, by
   giving the name column a zero flex-basis so it claimed only the width the meta run left over.
   Its comment argued that "an action row must never wrap". That is precisely what made the NAME
   wrap: a CJK region name squeezed into the leftover width collapsed to one character per line and
   became unreadable, which is the defect this replaces. The rule is now the opposite — the name
   never wraps, and the ROW takes a second line to honour that. Do not give the name column a
   shrinkable basis again; tests/journeyfit.test.mjs fails if it comes back. */
/* Rung three — 跑馬燈. The name is wider than a full line, so it scrolls instead of wrapping or
   being silently cut. Shift + duration are set per row by fitJourneyRow() from the real overflow.
   `alternate` walks back rather than snapping, so the start of the name is always reachable. */
.routine-jrow.is-marquee .routine-jname { animation: rjmarquee var(--rjm-dur, 8s) linear infinite alternate; }
@keyframes rjmarquee { from { transform: translateX(0); } to { transform: translateX(var(--rjm-shift, 0)); } }
@media (prefers-reduced-motion: reduce) {
  /* No scrolling for a reader who asked for stillness — the name is clipped instead, and the row's
     title attribute (set by fitJourneyRow) still carries it in full. */
  .routine-jrow.is-marquee .routine-jname { animation: none; }
}
.routine-jrow-meta .rt-realend { padding: 0; margin-left: 2px; line-height: 1; }
.rt-realend.on { color: var(--md-sys-color-tertiary); --fa-primary-color: var(--md-sys-color-tertiary); --fa-secondary-color: var(--md-sys-color-tertiary); }
.routine-jcut { display: flex; align-items: center; gap: 6px; margin: 8px 0 2px; padding-top: 6px; border-top: 1px dashed var(--md-sys-color-outline-variant); font-size: .74rem; font-weight: 600; color: var(--md-sys-color-on-surface-variant); }
.routine-jcut-n { color: var(--md-sys-color-on-surface-variant); font-weight: 500; }
.routine-realend { display: flex; align-items: center; gap: 6px; margin: 2px 0 4px; color: var(--md-sys-color-tertiary); }
.routine-card .tl-head .rt-map { margin-left: 6px; }
.routine-prog { margin: 8px 0; }
.routine-prog-top, .routine-prog-bot { display: flex; justify-content: space-between; align-items: center; font-size: .82rem; }
.routine-prog-top .rp-delta { margin-left: 4px; font-weight: 700; }
.routine-prog-bar { position: relative; height: 26px; margin: 4px 0; }
.routine-prog-bar .rp-canvas { position: absolute; left: 0; top: 7px; }
.routine-prog-bar .rp-icon { position: absolute; top: 0; transform: translateX(-50%); font-size: 18px; color: var(--owner, var(--md-sys-color-primary)); transition: left .4s ease; --fa-primary-color: var(--owner, var(--md-sys-color-primary)); --fa-secondary-color: var(--owner, var(--md-sys-color-primary)); --fa-secondary-opacity: .35; }
.routine-prog-cur { display: flex; align-items: center; gap: 6px; font-weight: 600; font-size: .88rem; margin-top: 2px; }
.routine-batt { display: flex; align-items: center; gap: 5px; }
.routine-blackout { position: fixed; inset: 0; z-index: 9999; background: #000; touch-action: none; -webkit-user-select: none; user-select: none; cursor: none; }
body.routine-blackout-on { overflow: hidden; }
.routine-setup-box { max-width: 460px; }
.routine-setup-body { display: flex; flex-direction: column; gap: 12px; max-height: 66vh; overflow-y: auto; }
.routine-grid2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px; }
.routine-caps { display: flex; flex-direction: column; gap: 6px; }
.routine-cap { font-size: .8rem; display: flex; align-items: flex-start; gap: 8px; color: var(--md-sys-color-on-surface-variant); }
.routine-cap.warn { color: var(--md-sys-color-error); }
.routine-templates { display: flex; flex-direction: column; gap: 6px; }
.routine-template-row { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: var(--r-md); background: var(--md-sys-color-surface-container); }
.routine-tpl-name { font-size: .85rem; font-weight: 600; display: inline-flex; align-items: center; gap: 5px; }
.routine-tpl-stk { margin-left: auto; display: inline-flex; gap: 4px; color: var(--md-sys-color-primary); }
.routine-stk-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(56px, 1fr)); gap: 8px; max-height: 60vh; overflow-y: auto; padding: 4px; }
.rt-stk { border: 2px solid transparent; border-radius: var(--r-md); background: var(--md-sys-color-surface-container-high); padding: 4px; cursor: pointer; }
.rt-stk img { width: 100%; display: block; border-radius: var(--r-md); }
.rt-stk[aria-pressed="true"] { border-color: var(--md-sys-color-primary); }
.rt-pick-one { justify-content: flex-start; }
/* ---- Health Check (v03r29; replaces the retired test harness) ---- */
#appIcon[role="button"] { cursor: pointer; }
#healthCheckCard > .card { background: var(--md-sys-color-surface-container); margin: 12px 0 0; }
