/* ------------------------------------------------------------------
   Duckr — app stylesheet.
   Every panel is Jesse's Plate.png, 9-sliced so the brass frame and corner
   rivets survive at any size. Stars, nav icons and buttons are separate
   slices; a 9-slice would stretch them.
   The comps were fixed 390x862 boxes. Here the appbar and tab bar are fixed
   to the viewport and the view scrolls between them, with safe-area insets
   for the notch and home indicator.
   ------------------------------------------------------------------ */
@import url('https://fonts.googleapis.com/css2?family=Saira:wght@400;500;600;700&family=Saira+Condensed:wght@300;400;500;600;700&family=Courier+Prime:wght@400;700&display=swap');

/* Sky behind the windshield, by local hour (set on <body> in app.js).
   Six bands, because sky changes are not linear in time — nearly all the
   variation clusters at the two transitions, so 24 images would be 20 nobody
   could tell apart. */
/* The interior is a variable too, so a relit frame (lit gauges after dark) is
   a one-line swap per band rather than a refactor. All bands point at the one
   frame until the dusk/night variants exist. */
body{--sky:url('/assets/dash-sky-midday.jpg'); --frame:url('/assets/dash-frame-day.png')}
body[data-tod="night"]  {--sky:url('/assets/dash-sky-night.jpg');
                         --frame:url('/assets/dash-frame-night.png')}
body[data-tod="dawn"]   {--sky:url('/assets/dash-sky-dawn.jpg')}
body[data-tod="morning"]{--sky:url('/assets/dash-sky-morning.jpg')}
body[data-tod="midday"] {--sky:url('/assets/dash-sky-midday.jpg')}
body[data-tod="golden"] {--sky:url('/assets/dash-sky-golden.jpg')}
/* After dark the cabin darkens and the gauges light up. */
body[data-tod="dusk"]   {--sky:url('/assets/dash-sky-dusk.jpg');
                         --frame:url('/assets/dash-frame-dusk.png')}

:root{
  --cream:#E8E2CF;
  --cream-dim:#C2BCA6;
  --tan:#A79E82;
  --gold:#C9A227;
  --brass:#B8963F;
  --mono:'Courier Prime',ui-monospace,monospace;
  --gap:14px;
  --safe-t:env(safe-area-inset-top,0px);
  --safe-b:env(safe-area-inset-bottom,0px);
  --bar-h:56px;
  --tab-h:132px;
}
*{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}
html,body{height:100%;overscroll-behavior:none}
body{
  background:#14160F; color:var(--cream);
  font-family:'Saira',-apple-system,system-ui,sans-serif;
  /* Fields under 16px make iOS zoom on focus, which then lets the page scroll
     sideways. Everything that takes typing is 16px; see .field. */
  -webkit-text-size-adjust:100%;
}
.bg{
  position:fixed; inset:0; z-index:0;
  background:url('/assets/tex-dash.jpg') center/cover;
}
.bg:after{
  content:""; position:absolute; inset:0;
  background:linear-gradient(180deg,rgba(22,26,16,.80),rgba(11,13,8,.92));
}
.grain{
  position:fixed; inset:0; z-index:60; pointer-events:none; opacity:.15;
  mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.95' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ---------- app bar ---------- */
.appbar{
  position:fixed; top:0; left:0; right:0; z-index:50;
  height:calc(var(--bar-h) + var(--safe-t)); padding-top:var(--safe-t);
  box-sizing:content-box;
  display:flex; align-items:center; justify-content:center;
  /* The gradient runs PAST the bar (the box is 30px taller than --bar-h via
     the padding below) so content scrolling up meets a long fall-off instead
     of a hard edge at the bar's boundary. */
  background:linear-gradient(180deg,rgba(15,17,10,.96) 0%,rgba(15,17,10,.92) 44%,
                                    rgba(15,17,10,.62) 72%,rgba(15,17,10,0) 100%);
  padding-bottom:30px; pointer-events:none;
}
.appbar > *{pointer-events:auto}
.appbar .word{
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:1px; line-height:1;
}
/* the real logo, on every screen — not the app name set in type. The bar is
   48px, so the mark sits BESIDE the wordmark rather than above it the way the
   lockup art stacks them. */
.appbar .word .lock{display:flex; align-items:center; gap:6px;
  filter:drop-shadow(0 1px 4px rgba(0,0,0,.95))}
.appbar .word .mk{height:30px; width:auto; display:block}
.appbar .word .wd{height:17px; width:auto; display:block}
/* Where you are heads the screen, not the bar. Set in the plate's voice so it
   reads as part of the app rather than as browser chrome. */
.page-title{
  margin:2px 0 14px; font-size:19px; font-weight:600;
  letter-spacing:.16em; text-indent:.16em; text-transform:uppercase;
  color:#EFE9D6; text-shadow:0 2px 5px rgba(0,0,0,.85), 0 1px 0 rgba(0,0,0,.7);
}
.rnd{
  position:absolute; top:calc(var(--safe-t) + 6px); height:36px; border:0;
  background:center/100% 100% no-repeat; cursor:pointer;
  filter:drop-shadow(0 2px 5px rgba(0,0,0,.6));
}
.rnd.l{left:16px}
.rnd.r{right:16px}
.rnd.back{width:40px; background-image:url('/assets/ui/isq-back-default.png')}
.rnd.plus{width:37px; background-image:url('/assets/ui/isq-plus-default.png')}
.rnd.dots{width:37px; background-image:url('/assets/ui/isq-dots-default.png')}
.rnd:active{filter:drop-shadow(0 1px 3px rgba(0,0,0,.7)) brightness(1.25)}

/* ---------- views ---------- */
/* No box at all on a phone: the app bar and tab bar keep pinning themselves
   to the window exactly as before. See the 768px block for what it becomes. */
#shell{display:contents}

.view{
  position:absolute; inset:0; z-index:10; display:none;
  overflow-y:auto; -webkit-overflow-scrolling:touch;
  padding:calc(var(--bar-h) + var(--safe-t) + 6px) 16px
          calc(var(--tab-h) + var(--safe-b) + var(--gap));
}
.view.on{display:block}
.stack{display:flex; flex-direction:column; gap:var(--gap)}

/* While typing (body.kb, set from a focusin listener) the fixed bars fade out:
   WKWebView pans the rendered surface to reveal the field, and panned fixed
   bars end up under the clock / mid-screen over the form. */
body.kb .appbar, body.kb .tabs{opacity:0; pointer-events:none; transition:opacity .18s}
.appbar, .tabs{transition:opacity .18s}

/* ---------- the 9-sliced plate ---------- */
.panel{
  border:22px solid transparent;
  border-image:url('/assets/panel9.png') 118 fill stretch;
  position:relative;
}
.panel.sm{border-width:10px}
.panel > .star{
  position:absolute; width:21px; height:24px; z-index:3;
  background:url('/assets/star.png') center/contain no-repeat;
}
.panel > .star.tl{left:10px; top:9px}
.panel > .star.tr{right:10px; top:9px}
.panel > .star.ml{left:12px; top:50%; margin-top:-12px}
.panel > .star.mr{right:12px; top:50%; margin-top:-12px}

.inset{
  background:linear-gradient(180deg,rgba(9,11,7,.62),rgba(14,17,10,.42));
  border-radius:4px;
  box-shadow:inset 0 2px 5px rgba(0,0,0,.62),
             inset 0 -1px 0 rgba(255,255,255,.07),
             0 1px 0 rgba(255,255,255,.05);
}

/* ---------- type ---------- */
.h-big{font-size:22px; font-weight:600; letter-spacing:.12em; text-indent:.12em;
       color:#EFE9D6; text-transform:uppercase; line-height:1.1;
       text-shadow:0 2px 4px rgba(0,0,0,.8)}
.h-sub{font-size:11px; font-weight:500; letter-spacing:.18em; text-indent:.18em;
       color:var(--tan); text-transform:uppercase}
.sec-lb{font-size:13px; font-weight:600; letter-spacing:.12em; text-indent:.12em;
        color:var(--cream); text-transform:uppercase; padding:2px 2px 9px}
.mono{font-family:var(--mono); color:#D6CFB6}

/* ---------- rows ---------- */
.rows{overflow:hidden}
.row{display:flex; align-items:center; min-height:29px;
     border-bottom:1px solid rgba(0,0,0,.45); box-shadow:0 1px 0 rgba(255,255,255,.045)}
.row:last-child{border-bottom:0; box-shadow:none}
.row .k{flex:0 0 42%; display:flex; align-items:center; gap:8px; padding:0 9px;
        font-size:9.5px; font-weight:600; letter-spacing:.14em; text-indent:.14em;
        color:var(--tan); text-transform:uppercase;
        border-right:1px solid rgba(0,0,0,.45)}
.row .v{flex:1; padding:0 10px; font-family:var(--mono); font-size:11px;
        color:#D6CFB6; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.row svg{flex:0 0 auto; opacity:.82}

/* ---------- buttons ---------- */
.btn{
  display:block; width:100%; border:0; cursor:pointer; background:center/100% 100% no-repeat;
  filter:drop-shadow(0 5px 12px rgba(0,0,0,.55));
}
.btn:active{filter:drop-shadow(0 2px 6px rgba(0,0,0,.6)) brightness(1.08)}
.btn.add    {aspect-ratio:2.991; background-image:url('/assets/ui/btn-add-default.png')}
.btn.save   {aspect-ratio:2.768; background-image:url('/assets/ui/btn-save-default.png')}
.btn.cancel {aspect-ratio:3.987; background-image:url('/assets/ui/btn-cancel-default.png')}
.btn.edit   {aspect-ratio:3.987; background-image:url('/assets/ui/btn-edit-default.png')}
.btn.del    {aspect-ratio:3.987; background-image:url('/assets/ui/btn-delete-default.png')}
.btn.remove {aspect-ratio:3.987; background-image:url('/assets/ui/btn-remove-default.png')}
.btn:disabled{opacity:.45; filter:grayscale(.4)}

/* ---------- tab bar ---------- */
.tabs{
  position:fixed; left:15px; right:15px; z-index:50;
  bottom:calc(var(--safe-b) + 8px); height:119.5px;
  background:url('/assets/tabbar-cut.png') center/100% 100% no-repeat;
  filter:drop-shadow(0 6px 14px rgba(0,0,0,.6));
  display:flex; padding:10px 3.89% 25px 2.05%;
}
.tab{
  flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:7px; color:var(--cream-dim); cursor:pointer; background:none; border:0;
  font-family:inherit;
}
.tab.on{color:var(--gold)}
.tab span{text-transform:uppercase; font-size:8.5px; font-weight:600;
          letter-spacing:.17em; text-indent:.17em; text-shadow:0 1px 3px rgba(0,0,0,.95)}
.tab svg{display:block; filter:drop-shadow(0 1px 2px rgba(0,0,0,.9))}

/* ================= DASH =================
   NOT a scrolling column. The comp is one composition: the dashboard photo
   fills the viewport and the plate, dial and tab bar sit ON it, over the lower
   dash. Letting them flow underneath the photo was what made this stop looking
   like the design. Positions are percentages of the view so it holds at any
   phone height; heights come from each asset's own aspect ratio. */
#v-dash{padding:0; overflow:hidden}
/* On a phone the stage IS the view — this is a no-op there. The wide-screen
   rules below are what it exists for. */
.dash-stage{position:absolute; inset:0}
/* The interior PNG, its windshield cut to alpha, with the sky on its own layer
   behind it — so a new time of day is a gradient swap rather than a whole new
   photograph, and the sky can move without the cabin moving with it. The art
   is 852x1847 — aspect .461 against the phone frame's .462 — so it goes
   full-bleed with no cropping.
   The sky is bled past every edge by --look-slack, which MUST stay larger than
   the largest translate lookaround.js applies (22px), or a tilt walks the
   image off its own edge and shows the stage behind it. */
.dash-hero{
  position:absolute; inset:0; z-index:1;
  background-image:var(--frame);
  background-size:100% 100%;
  background-position:center;
  background-repeat:no-repeat;
}
.dash-sky{
  --look-slack:30px;
  position:absolute; inset:calc(var(--look-slack) * -1); z-index:0;
  background:var(--sky) center/cover no-repeat;
  will-change:transform;
}
/* A user's own dash photo replaces the whole scene, frame and all. */
.dash-hero.custom{background-image:none; background-size:cover; background-position:center}
/* the shelf the ducks stand on, measured off the photo at y 0.4284-0.4583 */
/* Centre the row while it is narrower than the shelf; the auto margins
   collapse to zero once it overflows, so a long shelf still scrolls. */
.row-ducks > :first-child{margin-left:auto}
.row-ducks > :last-child{margin-right:auto}
/* Ducks stand ON the dash top. Re-measured for the regenerated frames: the
   windshield now ends at 40.9% and the ledge edge is at 45.0% (was 45.6/49.8),
   so feet land at 44.7% and they rise into the glass. All three frames agree
   to within 0.2%, so one shelf position serves every band. */
.shelf{
  position:absolute; left:0; right:0; top:38.1%; height:6.6%; z-index:2;
  overflow-x:auto; overflow-y:hidden; -webkit-overflow-scrolling:touch;
  scroll-snap-type:x proximity;
}
/* while the dial is being dragged: snapping mid-drag reads as judder */
.shelf.dragging{scroll-snap-type:none; scroll-behavior:auto}
.shelf::-webkit-scrollbar{display:none}
.shelf .row-ducks{display:flex; align-items:flex-end; height:100%; gap:14px; padding:0 16px}
.shelf .d{
  flex:0 0 auto; height:100%; aspect-ratio:1.06; scroll-snap-align:center;
  border:0; padding:0; appearance:none;
  background:center bottom/contain no-repeat; cursor:pointer;
  filter:drop-shadow(0 5px 5px rgba(0,0,0,.55)) brightness(.92) saturate(.95);
}
/* ---------- gear ---------- */
.gear-row{
  display:block; text-decoration:none; color:inherit; padding:14px 12px;
  border-bottom:1px solid rgba(0,0,0,.45); box-shadow:0 1px 0 rgba(255,255,255,.05);
}
.gear-row:last-child{border-bottom:0; box-shadow:none}
.gear-row:active{background:rgba(184,150,63,.08)}
.gt{display:flex; align-items:baseline; justify-content:space-between; gap:12px}
.gn{font-size:14px; font-weight:600; letter-spacing:.03em; color:var(--cream)}
.gp{font-family:var(--mono); font-size:11px; color:var(--tan); white-space:nowrap}
.gb{margin-top:5px; font-size:12.5px; line-height:1.55; color:var(--cream-dim)}

/* ---------- share ---------- */
/* The link is the point of the screen, so it is set in the mono face at a
   readable size and allowed to wrap mid-string — a token link is 40 characters
   and truncating it hides the half that differs. Selectable on purpose: press
   and hold is the fallback when the clipboard API is missing, which it is in
   some WebViews. */
.linkbox{
  font-family:var(--mono); font-size:13px; line-height:1.5; color:#D6CFB6;
  padding:12px 13px; word-break:break-all; -webkit-user-select:text; user-select:text;
}
.handle-set{
  font-family:var(--mono); font-size:13px; color:var(--cream-dim); padding-right:10px;
}
.handle-set b{color:var(--gold); font-weight:400}

/* One-time invitation to the tilt effect, sat low on the glass where the sky
   is plainest — above the shelf, so it never covers a duck. Deliberately
   quiet: this is a nicety, not an announcement. */
.look-hint{
  position:absolute; left:50%; top:30%; transform:translateX(-50%); z-index:4;
  border:1px solid rgba(184,150,63,.55); border-radius:999px;
  background:rgba(12,14,9,.62); -webkit-backdrop-filter:blur(3px); backdrop-filter:blur(3px);
  color:var(--cream-dim); font-family:var(--mono); font-size:10.5px;
  letter-spacing:.10em; text-transform:uppercase; padding:7px 15px 8px;
  cursor:pointer; animation:look-in .5s ease-out both .8s;
}
.look-hint.gone{opacity:0; pointer-events:none; transition:opacity .35s ease}
@keyframes look-in{from{opacity:0; transform:translate(-50%,6px)}
                   to{opacity:1; transform:translate(-50%,0)}}
@media (prefers-reduced-motion:reduce){.look-hint{animation:none}}
.dash-plate{
  position:absolute; left:1.3%; width:96.2%; top:59.5%; z-index:3;
  aspect-ratio:2.751;
  background:url('/assets/plate-cut.png') center/100% 100% no-repeat;
  display:flex; align-items:center; justify-content:center;
  filter:drop-shadow(0 7px 14px rgba(0,0,0,.6));
}
.dash-plate .txt{text-align:center; padding-bottom:2px}
.dash-plate .big{text-transform:uppercase; font-size:27px; font-weight:600;
  letter-spacing:.15em; text-indent:.15em; line-height:1; color:#EFE9D6;
  text-shadow:0 2px 4px rgba(0,0,0,.85), 0 1px 0 rgba(0,0,0,.8)}
.dash-plate .sub{text-transform:uppercase; margin-top:10px; font-size:11.5px;
  font-weight:500; letter-spacing:.30em; text-indent:.30em; color:#BDB49A;
  text-shadow:0 2px 4px rgba(0,0,0,.8)}
.dial{
  /* Centred by measuring a RENDER, not the boxes: the plate carries shadow
     padding below it and the tab bar above, so the boxes read 7/21 while the
     eye sees 55/33. Reading a single pixel column also lies — the plate's
     bottom edge is not flat. 76.4% puts ~44px of real gap on each side. */
  position:absolute; left:4.1%; width:91.8%; top:76.4%; z-index:3;
  aspect-ratio:7.048;
  background:url('/assets/dial-cut.png') center/100% 100% no-repeat;
  filter:drop-shadow(0 5px 11px rgba(0,0,0,.55));
  display:flex; align-items:center; justify-content:space-between; padding:0 4.4%;
}
.dial .chev{flex:0 0 auto; opacity:.92; background:none; border:0; cursor:pointer; padding:0}
/* The dial is a control, not a readout: drag it to scroll the shelf.
   touch-action:none stops the browser claiming the horizontal drag as a page
   gesture before the pointer handler sees it. */
.ticks{flex:1; height:74%; margin:0 4%; display:flex; align-items:center;
       justify-content:space-between; cursor:ew-resize; touch-action:none}
.ticks i{display:block; width:1.5px; height:46%; background:#8C7B4C; border-radius:1px}
.ticks i.tall{height:78%; background:#A08D58}
.ticks i.on{height:88%; width:2.4px; background:#E3B53F; box-shadow:0 0 6px rgba(227,181,63,.75)}

/* ================= CARDS ================= */
.stats{padding:2px 6px 4px; display:flex; align-items:center}
.stats > div{flex:1; text-align:center; padding:4px 0 5px}
.stats > div + div{border-left:1px solid rgba(0,0,0,.5);
                   box-shadow:-1px 0 0 rgba(255,255,255,.05)}
.stats .nv{display:flex; align-items:center; justify-content:center; gap:7px;
           font-size:19px; font-weight:600; color:#EFE9D6; line-height:1}
.stats b{display:block; font-size:8.5px; font-weight:600; letter-spacing:.16em;
         text-indent:.16em; color:var(--tan); text-transform:uppercase; margin-top:5px}
.sortbar{display:flex; align-items:center; justify-content:space-between; padding:0 2px}
.sortbar .s{font-size:10.5px; font-weight:600; letter-spacing:.13em; text-indent:.13em;
            color:var(--cream-dim); text-transform:uppercase; display:flex;
            align-items:center; gap:6px; background:none; border:0; font-family:inherit;
            cursor:pointer}
.toggle{display:flex; gap:4px}
.toggle button{width:30px; height:24px; border:0; border-radius:4px; display:flex;
       align-items:center; justify-content:center; color:var(--cream-dim);
       background:none; cursor:pointer}
.toggle button.on{color:var(--gold); background:rgba(201,162,39,.13);
                  box-shadow:inset 0 0 0 1.1px rgba(201,162,39,.5)}
.grid{display:grid; grid-template-columns:repeat(3,1fr); gap:7px}
.grid.list{grid-template-columns:1fr}
.card{border-width:8px; padding:1px 3px 2px; display:flex; flex-direction:column;
      min-height:128px; cursor:pointer; text-align:left; font-family:inherit}
.grid.list .card{min-height:0; flex-direction:row; align-items:center; gap:10px; padding:6px}
.card .ct{display:flex; align-items:flex-start; justify-content:space-between;
          font-size:9.5px; font-family:var(--mono); color:var(--cream-dim)}
.card .cn{font-size:8.5px; font-weight:600; letter-spacing:.06em; color:var(--cream);
          text-transform:uppercase; line-height:1.15; margin-top:1px; min-height:19px}
.card .cd{flex:1; background-position:center bottom; background-size:contain;
          background-repeat:no-repeat; margin:1px 0 2px; min-height:52px;
          filter:drop-shadow(0 3px 3px rgba(0,0,0,.5))}
.grid.list .card .cd{flex:0 0 46px; height:46px; margin:0}
.card .cf{border-top:1px solid rgba(0,0,0,.5); box-shadow:0 -1px 0 rgba(255,255,255,.05);
          padding-top:3px}
.grid.list .card .cf{border:0; box-shadow:none; padding:0; margin-left:auto; text-align:right}
.card .cf i{display:block; font-style:normal; font-size:6.5px; font-weight:600;
            letter-spacing:.12em; color:var(--tan); text-transform:uppercase}
.card .cf u{display:block; text-decoration:none; font-family:var(--mono);
            font-size:7.5px; color:#C5BEA6; margin-top:1px}
.grid.list .card .cn{font-size:12px; min-height:0}
.fav{background:none; border:0; padding:0; cursor:pointer; line-height:0}

/* ================= DETAIL ================= */
.hero{position:relative; height:214px; overflow:hidden; margin:0 -16px;
      background:url('/assets/hero-window.jpg') center 40%/cover no-repeat}
.hero .grad{position:absolute; inset:0;
  background:linear-gradient(180deg,rgba(10,12,8,.42),rgba(10,12,8,0) 40%)}
.hero .ledge{position:absolute; left:0; right:0; bottom:0; height:34px;
  background:linear-gradient(180deg,#585B4F,#2C2E25 38%,#1A1C15)}
.hero .ledge:before{content:""; position:absolute; left:0; right:0; top:0; height:2px;
  background:rgba(158,160,144,.55)}
.hero .duck{position:absolute; left:50%; bottom:28px; transform:translateX(-50%);
  width:150px; height:140px; background:center bottom/contain no-repeat;
  filter:drop-shadow(0 7px 7px rgba(0,0,0,.55))}
.count{position:absolute; right:14px; bottom:44px; width:104px; height:52px; z-index:4;
       display:flex; flex-direction:column; align-items:center; justify-content:center}
.count b{font-size:15px; font-weight:600; letter-spacing:.09em; text-indent:.09em;
         color:#EFE9D6; line-height:1.05}
.count i{font-style:normal; font-size:9px; font-weight:500; letter-spacing:.20em;
         text-indent:.20em; color:var(--tan); margin-top:2px}
.rec{padding:0 10px 10px}
/* Level with the duck's NAME, not floating between name and subtitle. The
   stars are absolute inside .panel, whose 22px border-image border offsets
   them, so top:0 here puts their centre on the name's centre (both at 34px
   from the panel's outer edge). */
.rec > .star.ml, .rec > .star.mr{top:0; margin-top:0}
.rec .name{text-align:center}
.rec .role{text-align:center; margin-top:2px}
.quote{margin-top:10px; padding:9px 11px 10px 28px; position:relative;
       font-family:var(--mono); font-size:11px; line-height:1.5; color:#CFC8AE}
.quote:before{content:"\201C"; position:absolute; left:9px; top:6px; font-size:24px;
              color:var(--brass); opacity:.85; line-height:1}
.rec .rows{margin-top:9px}
.rat{padding:0 8px 8px}
.rat .hd{display:flex; align-items:center; justify-content:center; gap:11px;
         padding:2px 0 8px; font-size:10.5px; font-weight:600; letter-spacing:.20em;
         text-indent:.20em; color:var(--cream-dim); text-transform:uppercase}
.rat .hd .s{width:11px; height:12px; background:url('/assets/star.png') center/contain no-repeat}
.cells{display:flex; gap:4px}
.cell{flex:1; text-align:center; padding:7px 1px 8px; border-radius:4px; min-height:44px}
.cell b{display:block; font-size:7px; font-weight:600; letter-spacing:.08em;
        text-indent:.08em; color:var(--tan); text-transform:uppercase; line-height:1.25}
.cell .st{margin-top:5px; font-size:8.5px; letter-spacing:.4px; color:var(--gold)}
.cell .st .off{color:#5A563F}

/* Jesse's duck glyph. A mask rather than an <img> so it inherits currentColor
   exactly like the inline SVG icons around it. */
.ico-duck{
  display:inline-block; flex:none; background:currentColor;
  -webkit-mask:url('/assets/ui/icon-duck.png') center/contain no-repeat;
          mask:url('/assets/ui/icon-duck.png') center/contain no-repeat;
}

/* ---------- in-app camera with the duck guide ----------
   Our own camera, because nothing can be drawn over the SYSTEM one the file
   input opens. */
body.cam-open{overflow:hidden}
.cam{position:fixed; inset:0; z-index:300; background:#000;
     display:flex; flex-direction:column; align-items:center; justify-content:center; gap:16px}
/* Square, because the capture keeps a centred square of the video: a
   full-screen preview would frame people against a crop they do not get. */
.cam-stage{position:relative; width:100%; aspect-ratio:1; overflow:hidden}
.cam-view{width:100%; height:100%; object-fit:cover; display:block}
.cam-guide{
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  pointer-events:none;
}
.cam-guide img{
  width:82%; height:auto; opacity:.9;
  filter:drop-shadow(0 0 5px rgba(0,0,0,.9)) drop-shadow(0 0 14px rgba(0,0,0,.6));
}
.cam-hint{
  order:-1; text-align:center; padding:0 28px; pointer-events:none;
  font-family:var(--mono); font-size:11.5px; letter-spacing:.1em;
  color:#EFE9D6; text-shadow:0 2px 6px rgba(0,0,0,.95);
}
.cam-bar{
  position:absolute; left:0; right:0; bottom:calc(var(--safe-b) + 26px);
  display:flex; align-items:center; justify-content:space-between; padding:0 26px;
}
.cam-shot{
  width:72px; height:72px; border-radius:50%; cursor:pointer;
  background:#EFE9D6; border:4px solid rgba(0,0,0,.45);
  box-shadow:0 0 0 3px rgba(239,233,214,.5);
}
.cam-shot:active{transform:scale(.94)}
.cam-x, .cam-lib{
  background:none; border:0; cursor:pointer; padding:10px 4px;
  font-family:var(--mono); font-size:11px; letter-spacing:.14em;
  text-transform:uppercase; color:#EFE9D6; text-shadow:0 2px 5px rgba(0,0,0,.9);
}
.cam-flip{
  position:absolute; right:20px; top:calc(var(--safe-t) + 18px);
  background:rgba(0,0,0,.45); border:1px solid rgba(239,233,214,.35);
  border-radius:99px; padding:7px 13px; cursor:pointer;
  font-family:var(--mono); font-size:10px; letter-spacing:.14em;
  text-transform:uppercase; color:#EFE9D6;
}

/* Cut-out vs original, offered after the model runs. */
.cut-choice{display:flex; gap:8px; margin-top:9px}
.cut-choice button{
  flex:1; padding:9px 4px; cursor:pointer; font-family:var(--mono);
  font-size:10px; letter-spacing:.14em; text-transform:uppercase;
  color:var(--cream-dim); background:rgba(0,0,0,.3);
  border:1px solid rgba(0,0,0,.5); border-radius:4px;
}
.cut-choice button.on{
  color:#241E12; background:linear-gradient(180deg,#DBCBA0,#C9B88A 46%,#A8946A);
  border-color:rgba(0,0,0,.45);
}
/* Flip is a toggle beside the pair, not a third choice — narrower so it does
   not read as an equal option. */
.cut-choice button[data-flip]{flex:0 0 76px}

/* ---------- user photos ----------
   A stock duck is a cut-out with no background, so `contain` floats it on the
   shelf exactly like a real duck. An uploaded PHOTO is a rectangle, and the
   same treatment turns it into a sticker propped on the dashboard — worse
   still sitting beside real cut-outs.
   Until uploads are actually cut out, a photo is presented AS a photo: a small
   print with a brass edge, standing on the ledge. Deliberate rather than
   broken. Everything here becomes dead the day segmentation lands. */
.shelf .d.photo{
  aspect-ratio:1.28; height:88%; align-self:flex-end;
  background-size:cover; background-position:center;
  border:2px solid rgba(201,162,39,.55); border-radius:3px;
  box-shadow:0 4px 6px rgba(0,0,0,.6), inset 0 0 0 1px rgba(0,0,0,.6);
  filter:drop-shadow(0 3px 3px rgba(0,0,0,.5)) brightness(.94) saturate(.95);
}
.card .cd.photo{
  background-size:cover; background-position:center;
  border-radius:3px; box-shadow:inset 0 0 0 1px rgba(201,162,39,.4);
  filter:none;
}
.hero .duck.photo{
  width:170px; height:126px; background-size:cover; background-position:center;
  border:3px solid rgba(201,162,39,.5); border-radius:4px;
  box-shadow:0 6px 14px rgba(0,0,0,.6);
  filter:none;
}
/* the avatar is a circle: a landscape photo under `contain` sat as a letterboxed
   strip across the middle of it */
.av i.photo{inset:6px; background-size:cover; background-position:center;
            border-radius:50%}

/* ---------- rating picker (detail) ---------- */
/* the category cells are buttons now */
.cell{-webkit-appearance:none; appearance:none; border:0; cursor:pointer; font-family:inherit; color:inherit}
.cell.picking{box-shadow:inset 0 0 0 1px rgba(201,162,39,.5)}
.rate-picker{
  margin-top:8px; padding:6px 10px; border-radius:4px;
  display:flex; align-items:center; gap:2px;
}
.rate-picker b{
  flex:1; text-align:left; font-size:9px; font-weight:600; letter-spacing:.14em;
  text-transform:uppercase; color:var(--tan);
}
.rstar{
  width:44px; height:44px; background:none; border:0; cursor:pointer;
  font-size:24px; line-height:1; color:#5A563F; padding:0;
}
.rstar.on{color:var(--gold); text-shadow:0 0 8px rgba(201,162,39,.35)}

/* ---------- filter chip (ducks) ---------- */
.chip{
  display:inline-flex; align-items:center; gap:7px; margin-right:10px;
  padding:7px 12px; border:1px solid rgba(184,150,63,.55); border-radius:99px;
  background:rgba(184,150,63,.12); cursor:pointer;
  font-family:var(--mono); font-size:10px; letter-spacing:.14em;
  text-transform:uppercase; color:var(--cream);
}
.chip b{color:var(--brass); font-size:12px; line-height:1}

/* ---------- received / wishlist / placed ----------
   Three BAKED states from Jesse's Pills.png (tools/slice-pills.py). The rims,
   rails, dividers, caps and rivets are his pixels, never reconstructed or
   stretched — overlaying CSS pills onto a rebuilt track was tried and every
   seam showed. The buttons here are transparent hit areas carrying only text,
   positioned on the slots the art draws: x 190-733, 780-1371, 1401-1971 of
   2172, and the pill band centred on y 323 of 724. */
.seg3{
  position:relative; width:100%; aspect-ratio:3.0;
  /* ONE sprite, three frames stacked: with three separate files the inactive
     states were not fetched until first tap, so the control blanked on the
     first switch. A single file cannot do that. */
  background:url('/assets/seg-pills.png') 0 0/100% 300% no-repeat;
  filter:drop-shadow(0 5px 12px rgba(0,0,0,.5));
}
.seg3[data-on="wishlist"]{background-position:0 50%}
.seg3[data-on="placed"]{background-position:0 100%}
.segb{
  position:absolute; top:24.9%; height:40.3%;
  display:flex; align-items:center; justify-content:center;
  background:none; border:0; padding:0; cursor:pointer;
  font-family:inherit; font-size:10.5px; font-weight:600;
  letter-spacing:.14em; text-indent:.14em; text-transform:uppercase;
  color:var(--cream-dim);
}
.segb[data-mode=received]{left:8.75%;  width:25.0%}
.segb[data-mode=wishlist]{left:35.91%; width:27.21%}
.segb[data-mode=placed]  {left:64.50%; width:26.24%}
.segb.on{color:#241E12; text-shadow:0 1px 0 rgba(255,255,255,.3)}

/* ---------- confirm sheet ---------- */
.cfm-wrap{
  position:fixed; inset:0; z-index:200; display:flex; align-items:center;
  justify-content:center; padding:28px; background:rgba(6,7,4,.72);
}
.cfm{width:100%; max-width:340px; padding:6px 8px 10px; text-align:center}
.cfm .t{font-size:16px; font-weight:600; letter-spacing:.08em; color:#EFE9D6;
  text-transform:uppercase; text-shadow:0 2px 4px rgba(0,0,0,.8); margin:6px 0 8px}
.cfm .b{font-family:var(--mono); font-size:11.5px; line-height:1.6; color:var(--cream-dim);
  margin:0 6px 4px}
.cfm .acts{display:flex; gap:10px; margin-top:10px; align-items:center}
.cfm .acts .btn-plate{font-size:12px; letter-spacing:.1em}

/* ---------- stats view ---------- */
.sbars{padding:10px 12px; border-radius:4px; display:flex; flex-direction:column; gap:9px}
.sbar{display:flex; align-items:center; gap:10px}
.sbar b{width:86px; flex:none; font-size:9.5px; font-weight:600; letter-spacing:.12em;
  text-transform:uppercase; color:var(--tan); text-align:left}
.sbar .tr{flex:1; height:8px; border-radius:99px; background:rgba(0,0,0,.45);
  box-shadow:inset 0 1px 2px rgba(0,0,0,.6); overflow:hidden}
.sbar .tr i{display:block; height:100%; border-radius:99px;
  background:linear-gradient(90deg,#8A7A4E,#C9A227)}
.sbar u{width:24px; flex:none; text-decoration:none; font-family:var(--mono);
  font-size:11px; color:var(--cream); text-align:right}
.beststar{
  display:flex; align-items:center; gap:14px; padding:10px 14px; border-radius:4px;
  background:none; border:0; cursor:pointer; font-family:inherit; text-align:left; width:100%;
}
.beststar .bd{width:52px; height:46px; flex:none; background:center/contain no-repeat;
  filter:drop-shadow(0 3px 6px rgba(0,0,0,.55))}
.beststar .bn b{display:block; font-size:13px; font-weight:600; letter-spacing:.1em;
  text-transform:uppercase; color:#EFE9D6}
.beststar .bn i{font-family:var(--mono); font-size:10px; color:var(--gold)}

/* ================= ADD / EDIT ================= */
.sec{padding:2px 10px 10px; display:flex; flex-direction:column}
/* The preview sits ABOVE .panel's border-image, which paints with `fill` and
   would otherwise cover it. A stock cut-out is a transparent duck on nothing,
   so it gets contain — cover would crop it to a slab of belly. */
.shot{position:relative}
.shot-img{position:absolute; inset:5px; border-radius:3px;
          background:center/cover no-repeat}
.shot-img.contain{background-size:contain}
.shot{min-height:150px; display:flex; flex-direction:column; align-items:center;
      justify-content:center; gap:12px; color:var(--cream-dim); cursor:pointer;
      background:center/cover no-repeat}
.shot span{font-size:11.5px; font-weight:600; letter-spacing:.17em; text-indent:.17em;
           text-transform:uppercase}
.shot.has span, .shot.has svg{display:none}
/* stock art keeps its label: it is a placeholder, and the panel must still
   read as tappable */
.shot .shot-img.stock ~ span{position:relative; z-index:1;
  text-shadow:0 2px 6px rgba(0,0,0,.9)}
.shot .shot-img.stock ~ svg{display:none}
.hint{margin-top:9px; padding:8px 10px 9px 32px; position:relative;
      font-family:var(--mono); font-size:9.5px; line-height:1.5; color:#C6BFA6}
.hint .s{position:absolute; left:11px; top:50%; margin-top:-9px; width:15px; height:17px;
         background:url('/assets/star.png') center/contain no-repeat}
[hidden]{display:none !important}
.frow{display:flex; align-items:stretch; min-height:44px;
      border-bottom:1px solid rgba(0,0,0,.45); box-shadow:0 1px 0 rgba(255,255,255,.05)}
.frow:last-child{border-bottom:0; box-shadow:none}
.fk{flex:0 0 42%; display:flex; align-items:center; gap:9px; padding:0 9px;
    color:var(--tan); border-right:1px solid rgba(0,0,0,.45); white-space:nowrap}
.fk span{font-size:9.5px; font-weight:600; letter-spacing:.11em; text-indent:.11em;
         color:var(--cream-dim); text-transform:uppercase}
.fk em{font-style:normal; font-size:8px; color:var(--tan); letter-spacing:.08em}
/* Jesse's brass switch, one slice per state. The two files are NOT the same
   shape — off is 189x107 (1.766) and on is 188x112 (1.679), because the brass
   knob sits proud of the rim — so the box is cut to the TALLER art and both
   are drawn `contain` inside it. Sizing each to its own aspect instead would
   jog the row 1.5px on every tap. */
/* The divider is there to separate a label from the field it belongs to. A
   switch row has no field, so it would just draw a line across empty leather. */
.swrow{justify-content:space-between; align-items:center}
.swrow .fk{flex:1 1 auto; border-right:0}
.sw{
  flex:0 0 auto; margin-right:10px; width:52px; aspect-ratio:1.679;
  border:0; padding:0; appearance:none; background:none; cursor:pointer;
  background:url('/assets/ui/toggle-off.png') center/contain no-repeat;
}
.sw[aria-checked="true"]{background-image:url('/assets/ui/toggle-on.png')}
.sw:disabled{opacity:.4; cursor:default}
/* 16px keeps iOS from zooming the page on focus — see the note on body. */
.field{flex:1; min-width:0; background:none; border:0; padding:0 10px; color:#D6CFB6;
       font-family:var(--mono); font-size:16px; outline:none}
.field::placeholder{color:#7E7864}
/* height:100% in a stretch row let the engine lay the text out against the
   full row box rather than centring it, and the chevron sits over the text's
   last character without the extra right padding. */
select.field{appearance:none; -webkit-appearance:none; cursor:pointer;
  align-self:stretch; padding-right:30px; line-height:normal;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23C2BCA6' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9.5 12 15.5 18 9.5'/%3E%3C/svg%3E") right 10px center/14px no-repeat}
textarea.field{padding-top:12px; resize:none; min-height:70px}

/* ================= PROFILE ================= */
.card-p{padding:4px 8px 8px; display:flex; align-items:center; gap:14px}
.av{flex:0 0 auto; width:84px; height:84px; margin-left:14px; border-radius:50%;
    background:radial-gradient(circle at 42% 34%,#4C4F3E,#23261C 72%); position:relative;
    box-shadow:inset 0 0 0 4px rgba(184,150,63,.55), inset 0 3px 9px rgba(0,0,0,.7);
    overflow:hidden}
.av i{position:absolute; inset:11px; background:center bottom/contain no-repeat}
.who{flex:1; min-width:0; padding-right:14px}
.who .n{font-size:15px; font-weight:600; letter-spacing:.05em; color:#EFE9D6;
        text-transform:uppercase; white-space:nowrap; overflow:hidden;
        text-overflow:ellipsis; line-height:1.15}
.who .r{font-size:10.5px; font-weight:500; letter-spacing:.16em; color:var(--tan);
        text-transform:uppercase; margin-top:3px; white-space:nowrap; overflow:hidden;
        text-overflow:ellipsis}
.who .hr{height:1px; background:rgba(0,0,0,.5); margin:10px 0 9px;
         box-shadow:0 1px 0 rgba(255,255,255,.06)}
.facts{display:flex}
.facts > div{flex:1}
.facts > div:first-child{flex:0 0 46%}
.facts > div + div{border-left:1px solid rgba(0,0,0,.5); padding-left:11px}
.facts b{display:block; font-size:7.5px; font-weight:600; letter-spacing:.10em;
         text-indent:.10em; color:var(--tan); text-transform:uppercase; white-space:nowrap}
.facts i{display:block; font-style:normal; font-family:var(--mono); font-size:10.5px;
         color:#D6CFB6; margin-top:3px; white-space:nowrap}
.menu{padding:0 6px; display:flex; flex-direction:column}
.mrow{display:flex; align-items:center; gap:12px; padding:11px 6px; cursor:pointer;
      background:none; border:0; font-family:inherit; text-align:left;
      border-bottom:1px solid rgba(0,0,0,.45); box-shadow:0 1px 0 rgba(255,255,255,.05)}
.mrow:last-child{border-bottom:0; box-shadow:none}
.mi{flex:0 0 26px; color:var(--tan); display:flex; justify-content:center}
.mt{flex:1}
.mt b{display:block; font-size:13px; font-weight:600; letter-spacing:.10em;
      text-indent:.10em; color:var(--cream); text-transform:uppercase}
.mt i{display:block; font-style:normal; font-family:var(--mono); font-size:10px;
      color:var(--tan); margin-top:2px; text-transform:uppercase}
.mc{flex:0 0 auto; color:var(--cream-dim)}

/* ---------- auth ---------- */
.auth{position:fixed; inset:0; z-index:70; display:none; overflow-y:auto;
      padding:calc(var(--safe-t) + 40px) 22px calc(var(--safe-b) + 40px)}
.auth.on{display:block}
.auth .wrap{max-width:400px; margin:0 auto}
.auth h1{text-align:center; font-size:26px; font-weight:600; letter-spacing:.34em;
         text-indent:.34em; text-transform:uppercase; color:var(--cream)}
.auth-mark{display:block; width:150px; height:auto; margin:0 auto 4px}
.auth .tag{text-align:center; font-size:10.5px; letter-spacing:.20em; text-indent:.20em;
           text-transform:uppercase; color:var(--tan); margin:8px 0 26px}
.auth .frow{min-height:48px}
.auth .swap{display:block; width:100%; margin-top:16px; text-align:center;
            background:none; border:0; cursor:pointer; font-family:inherit;
            font-size:11px; letter-spacing:.12em; text-transform:uppercase;
            color:var(--cream-dim); text-decoration:underline; text-underline-offset:4px}
.msg{margin-top:12px; padding:9px 12px; border-radius:5px; font-family:var(--mono);
     font-size:11px; line-height:1.45; display:none}
.msg.on{display:block}
.msg.err{color:#DFA091; border:1px solid #8A3D2C; background:rgba(138,61,44,.14)}
.msg.ok{color:#A9C9AE; border:1px solid #3F5B45; background:rgba(63,91,69,.14)}

/* The UI sheet has ADD DUCK / SAVE CHANGES / CANCEL / EDIT / DELETE / REMOVE —
   no SIGN IN. Rather than mislabel a button with someone else's words, this
   reuses the 9-sliced plate and sets the text in CSS. Swap to a raster the
   moment a SIGN IN plate exists. */
.btn-plate{
  display:block; width:100%; cursor:pointer; padding:13px 8px;
  /* a <button> has a default background that shows OUTSIDE the border-image,
     which reads as white bars either side of the plate */
  background:none; -webkit-appearance:none; appearance:none;
  border:22px solid transparent;
  border-image:url('/assets/panel9.png') 118 fill stretch;
  /* Matched to the baked EDIT DUCK label, measured off the art rather than
     eyeballed. Three things were wrong and only one was colour:
       WIDTH   the label is CONDENSED; this was regular-width Saira
       SIZE    its cap height is 23% of the plate (19px on an 82px plate),
               which is ~24px type — this was 16px
       TRACK   the letters are close together, not the .17em used here
     Colour is #F0E0C0 on a #392814 shadow, tighter below than above, which is
     what makes it read as raised metal rather than flat text. */
  font-family:'Saira Condensed','Saira',-apple-system,system-ui,sans-serif;
  /* Size is set PER PLATE below, not here: the three plates are different
     heights (brass 98, red 88, slate 80) and the baked art sets its label at
     23% of the plate's height. One fixed size therefore reads correct on one
     plate and small on the others — which is exactly how the brass ADD DUCK
     looked next to the baked EDIT DUCK. cap ~= .72em, so font = .23h / .72. */
  font-weight:400; letter-spacing:.10em;
  text-indent:.10em; text-transform:uppercase; color:#F0E0C0;
  text-shadow:0 -1px 0 rgba(255,255,255,.10),
              0 1px 0 #392814,
              0 2px 3px rgba(24,16,6,.75);
  filter:drop-shadow(0 5px 12px rgba(0,0,0,.55));
}
/* Jesse's blank control sheet, sliced to public/assets/ui9. These carry no
   baked label, so one plate serves any button.

   Slice numbers are measured, not guessed: on the 308x98 source the left rivet
   ends at x=39 and the right one starts at x=266, so 52px caps contain both —
   a narrower slice stretches a rivet into an oval. Border-width is ~0.55 of the
   slice because the button renders at about half the source's height. */
.btn-plate.brass, .btn-plate.slate, .btn-plate.red{
  /* Caps at 1:1 with the artwork — border-width EQUALS the slice, and the
     height EQUALS the art's own height, so nothing scales and the rivets stay
     round. THE THREE ARTS HAVE DIFFERENT GEOMETRY (brass 308x98, slate 311x80,
     red 312x88) — giving slate the brass numbers stretched its rivets 1.6x
     vertically. Each colour carries its own measured slice + height below;
     this shared block is layout only. */
  border-image-repeat:stretch;
  filter:none;                        /* the plate art carries its own shadow */
  padding:0; box-sizing:border-box;
  display:flex; align-items:center; justify-content:center;
}
/* Dark ink on a light plate is NOT the same treatment as cream on a dark one,
   and Jesse's own art does it differently too: btn-add's label is pure black
   and noticeably heavier, stamped INTO the brass with a soft dark halo — no
   light line anywhere. The white underline this had was pushing the letters
   out of the plate instead of into it, and 400 reads thin against a light
   ground where it reads correct against a dark one. */
.btn-plate.brass{border-image-source:url('/assets/ui9/brass-default.png');
  font-size:29px; font-weight:600;
  border-width:24px 52px 30px 52px; border-image-slice:24 52 30 52 fill; height:98px;
  color:#000; text-shadow:0 1px 2px rgba(60,40,14,.55), 0 0 6px rgba(60,40,14,.35)}
.btn-plate.brass:active{border-image-source:url('/assets/ui9/brass-pressed.png')}
.btn-plate.brass:disabled{border-image-source:url('/assets/ui9/brass-disabled.png')}

/* 700, not the shared 600: cream on a dark plate reads visibly lighter than
   near-black on brass at the same weight, so Log Out looked thin beside
   Add Duck. Same for the red plate. */
.btn-plate.slate{border-image-source:url('/assets/ui9/slate-default.png');
  font-size:26px;
  border-width:22px 58px 24px 58px; border-image-slice:22 58 24 58 fill; height:80px}
.btn-plate.slate:active{border-image-source:url('/assets/ui9/slate-pressed.png')}
.btn-plate.slate:disabled{border-image-source:url('/assets/ui9/slate-disabled.png')}

.btn-plate.red{border-image-source:url('/assets/ui9/red-default.png');
  font-size:28px;
  border-width:24px 56px 26px 56px; border-image-slice:24 56 26 56 fill; height:88px}
.btn-plate.red:active{border-image-source:url('/assets/ui9/red-pressed.png')}
.btn-plate.red:disabled{border-image-source:url('/assets/ui9/red-disabled.png')}

.btn-plate:active{filter:drop-shadow(0 2px 6px rgba(0,0,0,.6)) brightness(1.08)}
.btn-plate.brass:active,.btn-plate.slate:active,.btn-plate.red:active{
  filter:none; transform:translateY(1px)}
.btn-plate:disabled{opacity:.5}

/* ---------- social sign-in ---------- */
/* now sits between the social buttons and the email fields */
.or{display:flex; align-items:center; gap:12px; margin:16px 0 16px;
    font-family:var(--mono); font-size:10px; letter-spacing:.2em;
    text-transform:uppercase; color:var(--tan)}
.or:before,.or:after{content:""; flex:1; height:1px; background:rgba(0,0,0,.5);
    box-shadow:0 1px 0 rgba(255,255,255,.06)}
.socials{display:flex; flex-direction:column; gap:10px}
/* GIS bakes a fixed pixel width into its iframe, so this container is what
   social.js measures — do not give it a width that differs from the Apple
   button or the two will not line up. */
/* GIS's iframe is 44 tall around a 40px button, so it carries 2px of invisible
   padding top and bottom. Left alone that makes the gap under Google 12px and
   every other gap 10px. */
#btn-google{width:100%; min-height:40px; margin:-2px 0; display:flex; justify-content:center}
#btn-google iframe{margin:0 !important}
/* 40px and a 4px radius are NOT arbitrary: they are what GIS draws for
   size:'large', which is its tallest option and cannot be overridden. Measured
   off a capture of the live page, the Google plate is 40px tall with ~4px
   corners, so Apple matches it rather than the other way round. Both come out
   302 wide already, from the shared container width. */
.social-btn{
  width:100%; height:40px; display:flex; align-items:center; justify-content:center;
  gap:10px; border-radius:4px; cursor:pointer; font-family:inherit;
  font-size:13.5px; font-weight:600; letter-spacing:.02em;
  border:1px solid rgba(0,0,0,.5);
}
/* Native only. On the web this button is GIS's own iframe, so an unstyled .g
   never showed up in a browser — on device it rendered as default blue link
   text on white. These are Google's "filled black" values, which is the theme
   renderButton uses on the web, so the two paths look like one button. */
.social-btn.g{color:#E3E3E3; background:#131314; border-color:#8E918F;
  box-shadow:0 2px 5px rgba(0,0,0,.45)}
.social-btn.a{color:#0B0B0B; background:linear-gradient(180deg,#FFFFFF,#E9E9E6);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.9), 0 2px 5px rgba(0,0,0,.45)}
.social-btn:active{transform:translateY(1px); filter:brightness(.96)}

/* Cancel, and anything else that must be findable without competing with the
   plate above it. */
.quiet-link{
  display:block; width:100%; margin:2px 0 0; padding:12px 8px;
  background:none; border:0; cursor:pointer; font-family:var(--mono);
  font-size:11px; letter-spacing:.14em; text-transform:uppercase;
  color:var(--cream-dim); text-decoration:underline; text-underline-offset:4px;
}
.quiet-link:active{color:var(--cream)}

/* Account deletion (App Store 5.1.1(v)). Deliberately a plain link and not a
   plate: it must be findable, and it must not look like something to press. */
.danger-link{
  display:block; width:100%; margin:2px 0 4px; padding:12px 8px;
  background:none; border:0; cursor:pointer; font-family:var(--mono);
  font-size:11px; letter-spacing:.14em; text-transform:uppercase;
  color:#B4614C; text-decoration:underline; text-underline-offset:4px;
}
.danger-link:active{color:#D07A62}

/* ---------- empty state ---------- */
.empty{padding:22px 18px; display:flex; align-items:center; gap:18px}
.empty .t{font-family:var(--mono); font-size:11.5px; color:var(--cream-dim); line-height:1.6}

/* ---------- wide screens: the iPad rail, per the fleet standard ---------- */
@media (min-width:768px){
  /* Desktop and iPad get the iPhone layout, not a stretched one. Every screen
     in here is a fixed composition tuned to a phone — the dash most of all,
     whose shelf and plate sit at percentages measured off the photo — so the
     app runs at phone size in a centred frame rather than being reflowed.
     The transform is load-bearing: it makes #shell the containing block for
     the position:fixed app bar and tab bar, which would otherwise pin
     themselves to the window and escape the frame. */
  #shell{
    display:block; position:fixed; top:50%; left:50%;
    transform:translate(-50%,-50%);
    /* always leave a margin, or on a short window the frame runs flush to the
       edges and stops reading as a frame at all */
    width:390px; height:min(844px, calc(100vh - 56px));
    border-radius:22px; overflow:hidden;
    box-shadow:0 26px 70px rgba(0,0,0,.65), 0 0 0 1px rgba(0,0,0,.6),
               inset 0 0 0 1px rgba(233,226,207,.07);
  }
}
