/* Trip Logistics Organizer — branded skin (light/dark). See spec §12. */

/* Self-hosted fonts. Drop the .woff2 files into assets/fonts/ (see README).
   Falls back to system serif/sans if the files are absent. */
@font-face{font-family:'Inter';font-style:normal;font-weight:400 600;font-display:swap;src:url('fonts/Inter-Variable.woff2') format('woff2');}
@font-face{font-family:'Cormorant Garamond';font-style:normal;font-weight:600;font-display:swap;src:url('fonts/CormorantGaramond-SemiBold.woff2') format('woff2');}

:root{
  /* Shipped repaint (see Design_And_Architecture.md section 6, "Color system"
     and "Palette"): page/card moved from warm sand to a sage family that pairs
     with dark mode's plum on the color wheel. Border/hair are neutral re-tunes
     to go with them.

     paper-texture.png is multiplied over --page in light mode (see the
     html:not([data-theme="dark"]) body rule below), which darkens it somewhat.
     #F2F6EA is a partial-compensation midpoint between the flat "dried sage"
     #E8ECE1 and a fully-lightened #FBFFF3 (tried and rejected on their own).
     Dark mode's --page below has its own texture now too -- see the
     html[data-theme="dark"] body rule below and dark mode's own comment.

     Category accent colors (amber/violet/green below) -- second pass (see
     Design_And_Architecture.md section 6, "Card colors"). Bucket structure is
     now flight (amber) / ground+water transit: rail+car+bus+ferry (violet) /
     lodging (green) / dining+event+other (neutral, via --tint-neutral/
     --glow-neutral, both muted-gray tones built from --muted rather than a
     hue -- a 4th "color" so these cards get a border/glow like every other
     bucket instead of standing out as the one type with neither).
     Previously amber and the transit color ("coral", a muted red) were only
     ~22 degrees apart on the hue wheel -- hard to tell apart at a glance, and
     a textbook red-green colorblindness collision. The three hues below are
     spaced a full 120 degrees apart (amber ~24deg, green ~144deg, violet
     ~264deg) -- the maximum possible separation for three colors, chosen by
     eye against the actual sage background rather than a formal colorblind-
     safe palette tool, per the design discussion. */
  --page:#F2F6EA; --card:#F4F6F3; --border:#D2D9CB; --hair:#E6EAE2;
  --text:#333333; --text2:#6E6459; --muted:#A0937F;
  --accent:#E07B33; --accent-soft:#FBEEDC; --accent-border:#E0CBB0; --accent-text:#9A5B14;
  --danger:#B5443A;
  --c-amber:#E07B33; --c-violet:#6A45A1; --c-green:#428A5F;
  --glow-amber:rgba(224,123,51,.18); --glow-violet:rgba(106,69,161,.18); --glow-green:rgba(66,138,95,.16);
  --tint-amber:#F0D3B4; --tint-violet:#D6CBE6; --tint-green:#C4E3D1;
  /* Neutral bucket (dining/event/other): a 4th "color," but a de-saturated
     one built from --muted rather than a hue -- these types deliberately
     have no category color, so the card border/glow shouldn't read as
     "missing" a tint, just as calmer than the other three. */
  --glow-neutral:rgba(160,147,127,.16); --tint-neutral:#D9CFBC;
  --motif-arc:#D8C7B8; --motif-plane:#BBA88F;
  /* Average rendered color of the real textured page (flat --page composited
     with paper-texture.png's multiply blend), sampled directly from the
     composited output rather than guessed -- see the .statusbar-tint rule
     below and Design_And_Architecture.md section 6, "Home-screen status
     bar." Multiply only darkens, so this reads darker than --page alone. */
  --statusbar-tint:#E5E9DD;
  --serif:'Cormorant Garamond',Georgia,serif;
  --sans:'Inter',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
  /* layout.php's <meta name="color-scheme" content="light dark"> tells the
     browser either scheme is acceptable, so native form-control chrome (a
     date/time input's picker icon, a <select>'s arrow, any future checkbox
     or color/range input) follows the OS/browser's own dark-mode preference
     -- completely independent of this app's own data-theme, which is driven
     by local time of day, not the OS setting. Scoping color-scheme here to
     whichever theme is *actually* active fixes the mismatch (e.g. system in
     dark mode + app in light mode at midday used to render native-dark
     control chrome, like a near-black checkbox fill, on an otherwise light
     page -- originally noticed on the item form's now-removed All-day
     checkbox, but the fix is general and worth keeping). */
  color-scheme:light;
}
html[data-theme="dark"]{
  /* --page nudged darker than the original #1A1F36 (now #101320, same
     hue/saturation throughout -- #161B2E then #131727 were tried first and
     still read too light once the grain was on) to make room for
     paper-texture-dark.png (see the html[data-theme="dark"] body rule
     below): that PNG is screen-blended, which can only lighten, so starting
     darker keeps the composited result from landing lighter than the
     original plum. The grain itself is tinted toward blue/violet rather
     than neutral gray -- neutral grain on a colored dark base visibly
     desaturates it toward gray under screen blending, proportional to
     opacity, so the noise's own channel gains lean blue/violet to match
     --page's hue instead of washing it out. */
  --page:#101320; --card:#2B233D; --border:#2E2A44; --hair:#3C3352;
  --text:#FAF0E6; --text2:#B8AEC2; --muted:#8A80A0;
  --accent:#FFB74D; --accent-soft:#2E2A44; --accent-border:#3C3352; --accent-text:#FFB74D;
  --danger:#FF8080;
  /* Same three hue families as light mode (amber/violet/green), each re-tuned
     for a dark backdrop rather than reusing light mode's exact values --
     "consistent-ish between backgrounds," not identical. All three tints
     used to share one plain value (--hair) here, so category never showed on
     the border in dark mode, only via icon/glow -- now each gets its own.
     Violet needed the most rework: at light mode's hue (~264deg) it landed
     almost exactly on this theme's own --page/--card hue (~230-260deg,
     dark mode's plum family), so a translucent glow in that hue nearly
     vanished into the page it was sitting on -- barely a highlight at all.
     Fix was pushing it toward a more vivid, more magenta-leaning orchid
     (~290deg) at much higher saturation/lightness, so it reads as a distinct
     bright accent instead of "more plum," while staying in the same violet
     family as light mode rather than switching hues entirely. */
  --c-amber:#FFB74D; --c-violet:#D789E6; --c-green:#82C99F;
  --glow-amber:rgba(255,183,77,.22); --glow-violet:rgba(215,137,230,.24); --glow-green:rgba(130,201,159,.20);
  --tint-amber:#4A3B2A; --tint-violet:#45285C; --tint-green:#21452F;
  --glow-neutral:rgba(138,128,160,.18); --tint-neutral:#332E48;
  --motif-arc:#3A3556; --motif-plane:#5A4E78;
  /* Same idea as light mode's --statusbar-tint, sampled from --page composited
     with paper-texture-dark.png's screen blend -- screen only lightens, so
     this reads lighter/less-black than --page alone. */
  --statusbar-tint:#212232;
  color-scheme:dark;
}

*{box-sizing:border-box;}
html,body{margin:0;padding:0;}
body{background:var(--page);color:var(--text);font-family:var(--sans);font-size:15px;line-height:1.5;-webkit-font-smoothing:antialiased;}

/* Light-mode paper grain (see Design_And_Architecture.md section 6). A single
   tileable 512x512 PNG, rendered from the actual SVG feTurbulence filter graph
   (coarse mottling + fine grain, same values as the approved prototype) rather
   than approximated -- exported once via headless Chromium, not generated at
   runtime. Applied with background-blend-mode:multiply, which reproduces the
   same math as the two-layer mix-blend-mode prototype it was baked from. Scoped
   away from dark mode, which hasn't had a texture pass yet. */
html:not([data-theme="dark"]) body{
  background-image:url('paper-texture.png');
  background-repeat:repeat;
  background-blend-mode:multiply;
}

/* Dark-mode paper grain (see Design_And_Architecture.md section 6 and the
   --page comment above). Mirrors the light-mode technique but inverted:
   multiply can only darken, and dark mode's page is already close to black,
   so this uses background-blend-mode:screen (can only lighten) with a PNG
   that's mostly black with faint blue/violet-tinted highlights, instead of a
   PNG that's mostly white with faint darker specks. Rendered the same way
   (headless Chromium, feTurbulence stitchTiles for a seamless tile) with one
   extra step: the coarse+fine noise was rendered into a repeating <pattern>
   and blurred *after* tiling, then the center tile cropped back out --
   blurring a single already-stitched tile directly reintroduces a seam at
   its own edges (Gaussian blur reads past the tile boundary, which a lone
   feTurbulence primitive treats as transparent there), so the blur needs
   real neighboring tile data on both sides to stay seamless. */
html[data-theme="dark"] body{
  background-image:url('paper-texture-dark.png');
  background-repeat:repeat;
  background-blend-mode:screen;
}
a{color:var(--accent);text-decoration:none;}
.icon{vertical-align:-3px;}

.wrap{max-width:640px;margin:0 auto;padding:20px 18px 60px;padding-top:calc(20px + env(safe-area-inset-top,0px));}

/* Home-screen status-bar tint strip — see the <head> comment in layout.php
   and Design_And_Architecture.md section 6, "Home-screen status bar," for
   why this exists and what it can/can't achieve. Fixed, top-edge, no
   backdrop-filter and no background-image: this is deliberately the exact
   shape iOS 26 Safari's Liquid Glass tinting algorithm samples (a plain
   background-color on a position:fixed element within ~4px of the viewport
   top) rather than falling back to html/body's flat, uncompensated --page. */
.statusbar-tint{position:fixed;top:0;left:0;right:0;height:env(safe-area-inset-top,0px);background-color:var(--statusbar-tint);z-index:1000;pointer-events:none;}

/* Motif: undulating dashed flight-path spacer. height:88px on wider screens is
   tuned against the .wrap's up-to-640px width; the wave dives behind the first
   card via the negative bottom margin. */
.motif-band{position:relative;z-index:0;height:88px;margin:2px 0 -30px;pointer-events:none;}
.motif-band .path{position:absolute;inset:0;width:100%;height:100%;opacity:.4;}
.motif-band .plane{position:absolute;left:70%;top:16px;transform:translate(-50%,-50%) rotate(-6deg);opacity:.55;}
/* Below 480px the same 88px reads too tall: the path's viewBox is stretched to
   fill the band via preserveAspectRatio="none", so its vertical excursion is
   sized relative to the *box*, not the viewport -- holding height constant
   while width shrinks from up to 640px down to ~300-340px makes the curve
   read proportionally much steeper on a phone, even though no single number
   changed. Fix is a proportional scale-down of the whole band's geometry
   (height, both margins, and the plane's position), not just the height
   alone, so the curve's shape/overlap relationship to the first card stays
   the same as on wider screens, just smaller -- scale factor here is
   48/88 (~0.545): margin-top 2px->1px, margin-bottom -30px->-16px (keeps the
   same ~34% of the band tucked behind the first card), plane top 16px->9px
   (keeps the plane sitting on the same point of the curve, y=18 of the
   path's own 0-100 viewBox, scaled to the new height). This also reclaims
   ~26px of vertical space before the first card on mobile, which was the
   point -- not just a flatter curve but the card sitting higher. */
@media (max-width: 480px) {
  .motif-band{height:48px;margin:1px 0 -16px;}
  .motif-band .plane{top:9px;}
}
/* Moved off inline style="..." (layout.php's motif_band()) so the SVG markup
   stays clean under the CSP's style-src -- see Design_And_Architecture.md
   section 7. */
.motif-band svg.path path{stroke:var(--motif-arc);}
.motif-band svg.plane path{fill:var(--motif-plane);}
/* Cards/headers sit above the motif (positioned elements paint over static ones). */
.tripcard,.item,.dayhdr{position:relative;z-index:1;}

/* Header */
.topbar{display:flex;align-items:center;gap:10px;margin-bottom:4px;}
.topbar .back{color:var(--text2);display:inline-flex;}
/* Touch target (2026-09): these icon-only actions (back arrow, the trailing
   add-trip/add-item/settings/edit-trip mutelinks, and the "check email"
   iconbtn) had zero hit-area beyond their raw ~20px icon -- the entry form
   and settings page already got the ~44px-target treatment, but list.php/
   detail.php (arguably the two most-visited screens) hadn't. Same
   padding+matching-negative-margin technique as .rowline's already-fixed
   .x/.mutelink below: enlarges the tappable area without shifting the
   icon's own visual position or the row's layout. */
.topbar .back,.topbar>a.mutelink,.topbar .iconbtn{padding:10px;margin:-10px;}
.title{font-family:var(--serif);font-weight:600;color:var(--text);}
h1.title{font-size:26px;margin:0;line-height:1.05;}
.h-screen{font-size:23px;}
.subtitle{font-size:12px;color:var(--muted);margin:2px 0 18px;}
.spacer{flex:1;}

/* "En Route" topbar badge (detail.php) -- same look as the trip-card corner
   .stamp (same colors, rotation, type treatment) but laid out as a normal
   flex child in the topbar row rather than position:absolute, so it's its
   own class rather than a ".stamp" modifier. Deliberately NOT reusing
   ".stamp" here even as a shared base class: app.js's client-side override
   (for the trip-list cards) finds its targets via a plain ".stamp"
   selector and replaces the whole className wholesale when it swaps En
   Route/Departing Soon text -- sharing that class would risk this badge
   silently losing its layout (and gaining position:absolute inside a flex
   row) if that script ever ran against it. This badge also doesn't need the
   client-side date override to begin with: you only ever see it because
   index.php's bare-launch redirect already decided server-side, moments
   earlier, that this trip is en route right now. */
.topbar-stamp{
  /* Straight, not rotated -- the tilted treatment that works for a corner
     "stamp" on a trip card read as odd sitting mid-row next to plain,
     unrotated icons. align-items:center on .topbar (unchanged) centers it
     against the + / pencil icons' own height without anything extra needed
     here. */
  flex:0 0 auto; text-align:center;
  width:62px;
  font-size:8.5px; font-weight:700; letter-spacing:1px;
  text-transform:uppercase;
  padding:2px 0 1.5px;
  border-radius:2.5px;
  white-space:nowrap;
  line-height:1.3;
  pointer-events:none;
  margin:0 4px;
  color:var(--accent-text); background:var(--accent-soft); border:1.5px solid var(--accent-border);
}
html[data-theme="dark"] .topbar-stamp{background:var(--glow-amber);}

/* Tabs */
.tabs{display:flex;gap:6px;margin:16px 0 4px;}
.tab{font-size:13px;color:var(--muted);padding:5px 13px;border-radius:20px;}
.tab.on{color:var(--text);background:var(--card);border:1px solid var(--accent-border);}

/* Badge / chips */
.badge{font-size:11px;color:var(--accent-text);background:var(--accent-soft);border:1px solid var(--accent-border);padding:3px 9px;border-radius:20px;display:inline-flex;align-items:center;gap:4px;}

/* Trip card */
/* Squared card with the elevation shadow on a separately-rounded layer behind it
   (::before), since box-shadow always follows its own element's border-radius —
   this is how a squared card gets a rounder glow without a wrapper element. */
.tripcard{display:block;background:var(--card);border:1px solid var(--border);border-radius:4px;padding:13px 15px;margin-bottom:16px;color:inherit;}
.tripcard::before{content:"";position:absolute;inset:0;z-index:-1;border-radius:4px;box-shadow:0 6px 24px rgba(74,90,64,.20);}
.tripcard .name{font-family:var(--serif);font-size:22px;font-weight:600;color:var(--text);}
.tripcard .meta{font-size:14px;color:var(--muted);margin:1px 0 11px;}

/* "En Route" / "Departing Soon" corner stamp (trip_stamp() in helpers.php).
   position:absolute on purpose, not just for corner placement: .tripcard has
   no overflow/containment rule, and a positioned element always paints above
   a plain-flow sibling in the same stacking context regardless of DOM order
   -- so a long trip title that runs underneath the stamp is deliberately
   covered by it rather than the other way around, with no z-index needed to
   force it. z-index:2 below is set anyway, not to win that fight (already
   won) but to stay above .tripcard::before's own shadow layer and to make
   the stacking intent explicit for whoever edits this next. Fixed width +
   centered text, not auto-width, for both variants: a rotated box's corner
   swings further out the wider the box is, so if "En Route"/"Departing
   Soon" sized themselves to their own text, each would clear the card's top
   edge by a different amount -- fixed widths (verified against each
   label's actual rendered width, not guessed) keep that clearance identical
   no matter what the stamp says. */
.stamp{
  /* right:15px matches .tripcard's own padding exactly -- the same clearance
     "N items" gets from the card's bottom-right corner. top is *not* the
     matching 13px, though: rotating a wide, short box around its own center
     visually lifts its top edge above wherever `top` places the unrotated
     box -- top:13px measured ~8px of overlap past a 13px guide line.
     top:28px (clearing that guide by ~7px) measured correctly but looked
     too far down in the real card against the serif title's actual line
     height, which the synthetic test card didn't fully capture -- landed on
     20px (clearing the guide by ~1px, not 7) as the value that actually
     reads as "corner" rather than "floating," per on-device feedback. */
  position:absolute; top:20px; right:15px; z-index:2;
  width:62px; text-align:center;
  font-size:8.5px; font-weight:700; letter-spacing:1px;
  text-transform:uppercase;
  padding:2px 0 1.5px;
  border-radius:2.5px;
  transform:rotate(15deg);
  white-space:nowrap;
  line-height:1.3;
  color:var(--accent-text); background:var(--accent-soft); border:1.5px solid var(--accent-border);
}
/* "Departing Soon" wraps to two lines at a narrower width -- "Departing"
   alone needs about as much width as "Tomorrow" did in an earlier draft of
   this stamp, so fitting both words on one line would need an even wider
   box than En Route's, crowding the card corner more at this rotation.
   white-space:normal (overriding the nowrap above) lets it wrap at the
   natural word-space rather than needing a hard-coded <br> in list.php. */
.stamp.departing-soon{width:58px;font-size:7.8px;letter-spacing:.7px;line-height:1.25;padding:2.5px 0 2px;white-space:normal;}
/* Dark mode's --accent-soft (#2E2A44) sits almost on top of --card
   (#2B233D) -- fine for the "N to review" badge, which sits on --page, but
   the fill all but disappears on a card. --glow-amber is already the
   token this app uses for a translucent amber wash (the item-card glow
   layer), so reusing it here instead of inventing a one-off rgba() value. */
html[data-theme="dark"] .stamp{background:var(--glow-amber);}
.iconrow{display:flex;align-items:center;gap:13px;}
.iconrow .count{font-size:14px;color:var(--muted);margin-left:auto;}
/* Colors a type icon via the --ic custom property its neighboring .type-*
   class already sets (see the item-card rules below) -- moved off an inline
   style="color:var(--ic);display:inline-flex;" so list.php stays clean
   under the CSP's style-src (Design_And_Architecture.md section 7). */
.ic-swatch{color:var(--ic);display:inline-flex;}

/* Pending-review card (pending.php) -- same squared-card-plus-glow-layer
   shape as .tripcard/.item below, just without a category color (nothing to
   tint by, every pending item is "uncategorized" until acted on). This had
   no rule at all before (2026-09) -- .tcard rendered as a bare, unstyled div. */
.tcard{position:relative;z-index:1;background:var(--card);border:1px solid var(--border);border-radius:4px;padding:13px 15px;margin-bottom:15px;}
.tcard::before{content:"";position:absolute;inset:0;z-index:-1;border-radius:4px;box-shadow:0 6px 24px rgba(74,90,64,.20);}
.tcard-subj{font-size:16px;font-weight:600;color:var(--text);margin-top:8px;}
.tcard-from{font-size:14px;color:var(--muted);margin-top:2px;}

/* Day header */
.dayhdr{display:flex;align-items:baseline;gap:8px;margin:20px 0 10px;}
.dayhdr .d{font-family:var(--serif);font-size:22px;font-weight:600;color:var(--text);}
.dayhdr .rule{flex:1;height:0;border-bottom:1px solid var(--border);}

/* Itinerary item card */
.item{display:flex;gap:12px;align-items:flex-start;background:var(--card);border:1px solid var(--tint);border-radius:4px;padding:11px 12px;margin-bottom:15px;color:inherit;}
.item::before{content:"";position:absolute;inset:0;z-index:-1;border-radius:4px;box-shadow:0 5px 24px var(--glow);}
.type-flight{--tint:var(--tint-amber);--glow:var(--glow-amber);--ic:var(--c-amber);}
/* Transit bucket: every way of physically getting somewhere other than
   flying. Ferry moved here from the neutral bucket below -- it was neutral
   only because the buckets grew one type at a time, not by design; it's
   transit just like rail/car/bus. */
.type-rail,.type-car,.type-bus,.type-ferry{--tint:var(--tint-violet);--glow:var(--glow-violet);--ic:var(--c-violet);}
.type-lodging{--tint:var(--tint-green);--glow:var(--glow-green);--ic:var(--c-green);}
.type-event,.type-other,.type-dining{--tint:var(--tint-neutral);--glow:var(--glow-neutral);--ic:var(--muted);}
.item .tcol{width:58px;flex-shrink:0;align-self:stretch;display:flex;flex-direction:column;justify-content:center;text-align:center;}
.item .tbig{font-size:19px;font-weight:600;color:var(--text);font-variant-numeric:tabular-nums;line-height:1.2;}
.item .tz{font-size:11.5px;color:var(--muted);margin-top:2px;white-space:nowrap;}
.item .sep{width:0;align-self:stretch;border-left:1px solid var(--hair);}
/* Icon moved out of row1 into its own full-height column (see
   Design_And_Architecture.md section 6): sized to roughly match the tcol
   two-line block's height rather than the whole card, same align-self:stretch
   + centering approach tcol already uses. */
.item .icol{width:36px;flex-shrink:0;align-self:stretch;display:flex;align-items:center;justify-content:center;color:var(--ic);}
.item .body{min-width:0;flex:1;}
.item .row1{display:flex;align-items:center;gap:6px;}
.item .itype{font-size:16px;font-weight:600;color:var(--text);}
.item .isub{font-size:14px;color:var(--text2);margin-top:2px;}
.item .imeta{font-size:14px;color:var(--muted);margin-top:1px;}
.item.cancelled{opacity:.55;}
.item.cancelled .itype{text-decoration:line-through;}
/* Past items: flattened to a plain border with no glow at all regardless of
   category (--tint:var(--border), not --tint-neutral -- past should read as
   *less* present than even the neutral/dining bucket, not just same-as-
   neutral), via the --tint/--glow override, which wins here because this
   rule comes after the .type-* block in source order -- same specificity,
   later wins -- plus a faded whole-card opacity. The icon is recolored to
   --text rather than left in --muted, on the theory that a done item should
   still read clearly, just without its category color calling attention to
   it. Placed after .type-* and before .cancelled is irrelevant since
   .cancelled sets its own opacity and both can coexist. */
.item.past{--tint:var(--border);--glow:transparent;opacity:.55;}
.item.past .icol{color:var(--text);}

/* Add / centered link */
.addlink{text-align:center;margin-top:26px;}
.addlink a{font-size:13px;font-weight:500;color:var(--accent);}

/* Buttons */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;font-family:var(--sans);font-size:14px;font-weight:500;border-radius:9px;padding:9px 14px;cursor:pointer;border:1px solid var(--accent-border);background:var(--accent-soft);color:var(--accent-text);}
.btn:hover{filter:brightness(.98);}
.btn.secondary{background:transparent;border-color:var(--border);color:var(--text2);}
.btn.danger{background:transparent;border-color:transparent;color:var(--danger);}
.btn.full{width:100%;}
.btnrow{display:flex;gap:8px;}
/* Two tiny one-off spacing exceptions (pending.php's button row needs space
   above it, raw.php's needs space below) -- not a general utility-class
   system, just these two specific call sites, added while moving their
   inline style="margin-..." out for the CSP's style-src. */
.btnrow.mt-12{margin-top:12px;}
.btnrow.mb-8{margin-bottom:8px;}

/* Forms */
.field{margin-bottom:12px;}
.field label{display:block;font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.4px;margin:0 0 4px;}
.row{display:flex;gap:8px;}
.row > *{flex:1;}
/* min-height:44px is the standard minimum comfortable touch target -- these
   were unconstrained before (just their own padding), meaning every input in
   the add/edit item form was a harder tap target on a phone than it needed
   to be. Padding bumped to match (see Design_And_Architecture.md section 6,
   "Add/edit item form"). */
input[type=text],input[type=password],input[type=date],input[type=time],select,textarea{
  width:100%;font-family:var(--sans);font-size:14px;color:var(--text);background:var(--card);
  border:1px solid var(--accent-border);border-radius:8px;padding:12px 10px;min-height:44px;}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--glow-amber);}
textarea{resize:vertical;min-height:60px;}
.sectlabel{font-size:11px;font-weight:600;color:var(--text2);margin:18px 0 8px;display:flex;align-items:center;gap:5px;}
/* Sticky so Save/Cancel stay reachable while scrolling a long item form
   without hunting back up to the top -- needs an opaque backdrop so content
   doesn't visibly show through underneath it while scrolled. A flat
   background-color alone (the original fix here) left a plain, untextured
   horizontal strip cutting across the grain that covers the rest of the
   page -- the theme-scoped rules below give it the exact same paper-texture
   treatment as html/body (see that rule, further down) so the sticky strip
   reads as part of the same paper rather than a hole in it. This can't be
   pixel-perfectly continuous with the body's own texture once you actually
   scroll (a sticky element is fixed to the viewport while body's background
   scrolls with the page underneath it), but the grain is subtle/low-opacity
   enough that the two independently-tiled instances reads as "the same
   paper," not a visible seam. */
.formhdr{display:flex;align-items:center;justify-content:space-between;margin-bottom:18px;position:sticky;top:0;z-index:5;background-color:var(--page);padding:10px 0;}
.formhdr .save{font-size:14px;font-weight:600;color:var(--accent);background:none;border:none;cursor:pointer;font-family:var(--sans);}
.formhdr .cancel{font-size:13px;color:var(--text2);}
html:not([data-theme="dark"]) .formhdr{
  background-image:url('paper-texture.png');
  background-repeat:repeat;
  background-blend-mode:multiply;
}
html[data-theme="dark"] .formhdr{
  background-image:url('paper-texture-dark.png');
  background-repeat:repeat;
  background-blend-mode:screen;
}
.dangerzone{border-top:1px solid var(--border);margin-top:8px;padding-top:14px;text-align:center;}

/* Icon-tile type picker (add/edit item form). Selection state comes from the
   underlying native radio's :checked, via the adjacent-sibling selector below
   -- the tile <label> itself carries no "selected" class, so it can never go
   stale relative to which radio is actually checked. The radio is visually
   hidden (not display:none) so it stays in the tab order and keyboard/screen
   reader users get normal native radio-group behavior for free. */
.typegrid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;position:relative;}
.typerow-wrap{margin-bottom:6px;}
.typetile-input{position:absolute;opacity:0;width:1px;height:1px;overflow:hidden;pointer-events:none;}
.typetile{display:block;border:1px solid var(--border);border-radius:8px;padding:10px 4px;text-align:center;background:var(--card);cursor:pointer;min-height:44px;}
.typetile .icon{width:26px;height:26px;margin:0 auto 6px;display:block;}
.typetile .lbl{font-size:11px;color:var(--text2);display:block;}
/* Icon color always reflects the type's card-color bucket (see type_bucket()
   in helpers.php), selected or not -- only the tile's own border/background
   change on selection. Matches how the item cards themselves work. */
.typetile[data-bucket="amber"] .icon{color:var(--c-amber);}
.typetile[data-bucket="violet"] .icon{color:var(--c-violet);}
.typetile[data-bucket="green"] .icon{color:var(--c-green);}
.typetile[data-bucket="neutral"] .icon{color:var(--muted);}
.typetile-input:checked + .typetile{border-width:2px;}
.typetile-input:checked + .typetile[data-bucket="amber"]{border-color:var(--c-amber);background:var(--tint-amber);}
.typetile-input:checked + .typetile[data-bucket="violet"]{border-color:var(--c-violet);background:var(--tint-violet);}
.typetile-input:checked + .typetile[data-bucket="green"]{border-color:var(--c-green);background:var(--tint-green);}
.typetile-input:checked + .typetile[data-bucket="neutral"]{border-color:var(--border);background:var(--card);}
.typetile-input:checked + .typetile .lbl{color:var(--text);font-weight:600;}
.typetile-input:focus-visible + .typetile{outline:2px solid var(--accent);outline-offset:2px;}

/* Toggle-links (More types / Arrival differs) and the "More details"
   disclosure -- all 3 collapsibles in the add/edit item form share the same
   .inset box style. The .open class here is what makes each one visible
   before app.js has run at all (server-rendered open/closed per
   element_form.php); app.js's own refreshFormForType() re-derives the same
   result as an inline style afterward, since a collapsible that's also
   type-scoped (the flight-only "Arrival differs" box) needs both checks
   combined, not just this class alone. */
/* "More details" now reuses this exact same look (see element_form.php) --
   it used to be a separate bordered ".disclosure" button, which read as an
   unrelated boxed control next to "More types"/"Arrival differs" right above
   it. One convention for all 3 expanders now. */
/* display:block (not inline-block): when a collapsible sitting between two
   toggle-links is closed (display:none, zero height -- e.g. flight's
   "Arrival differs" box between the "Arrival differs" and "More details"
   toggles), inline-block siblings with nothing rendering between them just
   flow onto the same line together. Block forces each onto its own line
   regardless of what's collapsed around it. */
.toggle-link{font-size:12px;color:var(--accent-text);background:none;border:none;padding:0;display:block;margin:2px 0 14px;cursor:pointer;font-family:var(--sans);}
.toggle-link:before{content:"+ ";}
/* Two spaces after the escape, not one: a CSS hex escape like \2212 consumes
   exactly one trailing whitespace character as its own terminator (that's
   how the parser knows the hex digits ended), so a single trailing space
   here would be swallowed and never rendered, producing "−More types" with
   no gap -- the "+ " case above never had this problem since "+" is a plain
   quoted character, not a hex escape. */
.toggle-link[aria-expanded="true"]:before{content:"\2212  ";}
.inset{display:none;border:1px solid var(--accent-border);border-radius:8px;padding:12px;margin:2px 0 16px;background:var(--accent-soft);}
.inset.open{display:block;}

/* Mobile: stack every .row into one column, except .row.fromto (From/To) --
   a short, closely-related pair of plain text inputs that stays legible
   side by side even on a phone. This is the app's first real breakpoint;
   everything above 480px (including the plain desktop browser case) keeps
   the original always-side-by-side .row layout.

   .row.timerow (date+time[+all-day]) was *also* meant to be an exception
   here, same idea as From/To -- but unlike From/To's plain text inputs, a
   native date input and a native time input each have their own intrinsic
   minimum rendering width for their internal segments (month/day/year,
   hour/minute/AM-PM), which a flex-shrink or flex:1 share can't reduce.
   Squeezed to roughly half a ~350px phone width each, they visually
   overlapped/clipped instead of gracefully shrinking the way text does.
   Rather than fight that, .row.timerow just isn't an exception anymore --
   it falls through to the same full one-per-line stacking as any other
   .row, which gives each date/time input the full width to render in. */
@media (max-width: 480px) {
  .row{flex-direction:column;}
  .row.fromto{flex-direction:row;align-items:flex-end;}
}

/* Settings rows */
.section{font-size:13px;font-weight:600;color:var(--text);margin:22px 0 3px;display:flex;align-items:center;gap:5px;}
/* A .section header can also be a collapse/expand toggle (settings.php's
   "Signed-in devices") -- reset the native <button> chrome so it inherits
   .section's own look exactly, add a trailing chevron that rotates open,
   and full-width so the whole row is the tap target, not just the text. */
button.section{background:none;border:none;padding:0;width:100%;text-align:left;font-family:var(--sans);cursor:pointer;}
button.section .chev{margin-left:auto;color:var(--muted);flex-shrink:0;transition:transform .15s ease;}
button.section[aria-expanded="true"] .chev{transform:rotate(90deg);}
/* Body belonging to a collapsed-by-default .section (as opposed to .inset,
   which additionally adds a bordered/tinted box -- this is plain, so a
   collapsed section's content still looks like every other settings row
   once opened). */
.section-body{display:none;}
.section-body.open{display:block;}
.section .icon{color:var(--accent);}
.rowline{display:flex;align-items:center;justify-content:space-between;padding:11px 0;border-top:1px solid var(--border);gap:12px;}
/* .last is explicit (applied only to the one row that actually wants a
   bottom border, i.e. the last row of the last section) rather than the
   ":last-of-type" this replaced -- with every row on this page a sibling
   <div> flat under the same .wrap (no per-section wrapper), ":last-of-type"
   matched the literal last <div> in the whole page's DOM order, which was
   actually .addlink (the sign-out button), not any rowline -- a pre-existing
   bug, now fixed by being explicit instead. */
.rowline.last{border-bottom:1px solid var(--border);}
/* min-width:0 lets a long value (a long forwarding address, say) truncate/
   wrap within its own flex item instead of pushing the row's right-hand
   action (Remove, Set, chevron) out past the edge of the screen. */
.rowtext{min-width:0;}
.rowsub{font-size:11px;color:var(--muted);margin-top:1px;}
/* raw.php's email list -- a title that needs to truncate rather than wrap
   or overflow. Moved off inline style="..." for the CSP's style-src. */
.rowtitle{font-size:14px;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.identity{display:flex;align-items:center;gap:10px;}
.actionform{display:flex;gap:6px;align-items:center;}
.label-field{max-width:90px;}
.pw-field{max-width:130px;}
.daysel{width:90px;}
/* min-width:44px keeps an icon-only button (Remove, "view emails" chevron) at
   a comfortable tap size without visually growing it -- the extra hit area
   comes from padding, pulled back in with a matching negative margin so nothing
   shifts the row's layout. */
.rowline .x,.rowline > a.mutelink{padding:10px;margin:-10px;}
.avatar{width:30px;height:30px;border-radius:50%;background:var(--accent-soft);color:var(--accent-text);display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:600;}
/* Sender/user "add" forms: on a narrow screen the primary field (address,
   username) gets its own full-width line; the secondary field and the Add
   button share the line below rather than all 3 squeezing into one row. At
   normal widths flex-wrap simply never triggers, so this is a no-op there. */
.inline-add{display:flex;gap:8px;margin-top:11px;flex-wrap:wrap;}
.inline-add > :first-child{flex:1 1 100%;}
.inline-add > :not(:first-child):not(button){flex:1 1 120px;}
.inline-add > button{flex:0 0 auto;}

/* .rowline.stack: for the 2 rowlines whose right side is itself a small form
   (New password + Set; the day-count select + Save) rather than a single
   control -- those don't fit next to a label+subtitle block at phone width.
   Plain .rowline (no .stack) is left alone: raw.php's email list relies on
   it staying a single truncating line with the chevron pinned to the right,
   and the Theme/"Recent emails" rows here are short enough to never need it.
   Same 480px breakpoint as the item form's .row, so there's one breakpoint
   value for the whole app. */
@media (max-width: 480px) {
  .rowline.stack{flex-direction:column;align-items:stretch;gap:8px;}
  .rowline.stack .actionform{width:100%;}
  .rowline.stack .actionform .pw-field,
  .rowline.stack .actionform .daysel{max-width:none;width:auto;flex:1;}
}

/* Login */
.login-card{max-width:340px;margin:12vh auto 0;background:var(--card);border:1px solid var(--border);border-radius:16px;padding:26px 22px;box-shadow:0 4px 18px rgba(120,90,50,.10);}
.login-card h1{font-family:var(--serif);font-size:26px;font-weight:600;margin:0 0 4px;text-align:center;}
.login-card p{font-size:12px;color:var(--muted);text-align:center;margin:0 0 20px;}
.error{background:rgba(181,68,58,.10);border:1px solid rgba(181,68,58,.30);color:var(--danger);font-size:13px;border-radius:8px;padding:9px 11px;margin-bottom:14px;}

.raw{background:var(--page);border-radius:8px;padding:9px 10px;margin-top:10px;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:11px;color:var(--text2);line-height:1.5;white-space:pre-wrap;word-break:break-word;}
.empty{text-align:center;color:var(--muted);font-size:14px;padding:40px 10px;}
/* Off-screen but still selectable (for the copy button's fallback path). */
.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;}
.rawbox{width:100%;box-sizing:border-box;min-height:320px;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px;color:var(--text);background:var(--card);border:1px solid var(--accent-border);border-radius:10px;padding:10px;line-height:1.5;resize:vertical;}
.rowlink{text-decoration:none;color:inherit;}
.mutelink{color:var(--muted);font-size:13px;}
.iconbtn{background:none;border:none;cursor:pointer;padding:0;color:var(--muted);display:inline-flex;align-items:center;}
.iconbtn:hover{color:var(--accent);}
/* Wraps a lone .iconbtn <form> (list.php's "check email" button) so it sits
   inline in .topbar's flex row instead of taking a full block line. Moved
   off inline style="display:inline;line-height:0;" for the CSP's
   style-src. */
.iconform{display:inline;line-height:0;}
.flash{font-size:13px;color:var(--accent-text);background:var(--accent-soft);border:1px solid var(--accent-border);border-radius:10px;padding:8px 12px;margin:10px 0 0;display:flex;align-items:center;gap:6px;}
.flash.err{color:var(--danger);background:transparent;border-color:var(--danger);}
