Three single-file HTML apps ported to Makepad 2.0 (dev b41e740) script_mod! DSL: - rider (ride.html): 21 unit + 9 integration + 9 headless UI tests - koboyo (koboyo.html): 29 + 14 + 8 - insurance (insurance.html): 28 + 14 + 8 All 140 tests green. Includes headless-Linux platform patch, full README, setup script, and three brutal codebase assessments with execution plans.
1196 lines
No EOL
53 KiB
HTML
1196 lines
No EOL
53 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
|
||
<title>WWDC – 4-icon pill nav + FAB · pull sheet</title>
|
||
<style>
|
||
/* ========== RESET / TOKENS ========== */
|
||
*,*::before,*::after{box-sizing:border-box;margin:0}
|
||
html,body{height:100%}
|
||
body{
|
||
margin:0;
|
||
font-family:-apple-system,BlinkMacSystemFont,"SF Pro Text","SF Pro Display",Inter,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
||
background:#cecfd4;
|
||
color:#18181e;
|
||
-webkit-font-smoothing:antialiased;
|
||
-moz-osx-font-smoothing:grayscale;
|
||
}
|
||
button{font:inherit;color:inherit}
|
||
a{color:inherit}
|
||
|
||
/* ========== MOBILE APP SHELL – 390px ========== */
|
||
.app-shell{
|
||
width:390px;
|
||
max-width:100vw;
|
||
min-height:100dvh;
|
||
margin:0 auto;
|
||
background:#f2f2f5;
|
||
position:relative;
|
||
overflow:hidden;
|
||
display:flex;
|
||
flex-direction:column;
|
||
}
|
||
@media(min-width:440px){
|
||
body{background:linear-gradient(180deg,#dddde2,#b8b8be);display:flex;align-items:center;justify-content:center;padding:22px}
|
||
.app-shell{
|
||
height:812px;
|
||
min-height:auto;
|
||
border-radius:40px;
|
||
box-shadow:0 30px 80px rgba(0,0,0,.26), inset 0 0 0 9px #111217, inset 0 0 0 10px #2a2a30;
|
||
}
|
||
.island{display:block}
|
||
}
|
||
.island{display:none;position:absolute;top:10px;left:50%;transform:translateX(-50%);width:96px;height:30px;background:#111;border-radius:18px;z-index:90}
|
||
@media(min-width:440px){.island{display:block}}
|
||
.statusbar{
|
||
height:50px;
|
||
flex:0 0 50px;
|
||
display:flex;align-items:flex-end;justify-content:space-between;
|
||
padding:0 22px 7px;
|
||
font-size:14.5px;font-weight:600;color:#2b2b32;
|
||
}
|
||
|
||
/* ---------- article / feed ---------- */
|
||
.feed{
|
||
flex:1;
|
||
overflow-y:auto;
|
||
-webkit-overflow-scrolling:touch;
|
||
padding:8px 18px 128px;
|
||
}
|
||
.feed::-webkit-scrollbar{display:none}
|
||
.feed-top{
|
||
display:flex;justify-content:space-between;align-items:center;
|
||
margin:2px 2px 14px;
|
||
font-size:15px;color:#8a8a92;
|
||
}
|
||
.feed-brand{color:#e52b2b;font-weight:800;font-size:20px;letter-spacing:-.3px}
|
||
.hero{display:flex;gap:13px;align-items:flex-start}
|
||
.thumb{
|
||
width:108px;height:108px;flex:0 0 108px;
|
||
border-radius:24px;
|
||
background:#0f1015;
|
||
position:relative;
|
||
overflow:hidden;
|
||
box-shadow:0 8px 24px rgba(0,0,0,.14);
|
||
}
|
||
.thumb i{
|
||
position:absolute;left:13px;top:16px;
|
||
font-style:normal;font-weight:800;font-size:28px;line-height:.92;
|
||
color:#b8b8bf;opacity:.5;letter-spacing:-1px;
|
||
}
|
||
.thumb b{
|
||
position:absolute;bottom:13px;left:13px;
|
||
font-weight:800;font-size:38px;color:#fff;
|
||
text-shadow:0 0 16px rgba(255,255,255,.9);
|
||
letter-spacing:-1.5px;
|
||
}
|
||
h1.headline{
|
||
font-size:20.8px;line-height:1.26;font-weight:750;letter-spacing:-.4px;
|
||
color:#1b1b20;
|
||
}
|
||
.byline{
|
||
display:flex;align-items:center;gap:9px;
|
||
margin-top:11px;font-size:13.2px;color:#74747c;
|
||
}
|
||
.ava{width:30px;height:30px;border-radius:50%;background:linear-gradient(140deg,#705035,#2a1810);flex:0 0 30px}
|
||
.byline strong{color:#2a2a32;font-weight:600}
|
||
|
||
/* =====================================================
|
||
1) BOTTOM NAV — nav1.png / nav2.png
|
||
4 ICONS — pill left — FAB right — SAME ROW
|
||
===================================================== */
|
||
.nav-dock{
|
||
position:absolute;
|
||
left:14px; right:14px; bottom:20px;
|
||
z-index:30;
|
||
}
|
||
.nav-row{
|
||
display:flex;
|
||
flex-direction:row;
|
||
flex-wrap:nowrap;
|
||
align-items:center;
|
||
justify-content:space-between;
|
||
gap:13px;
|
||
width:100%;
|
||
}
|
||
|
||
/* pill – white, heavily styled */
|
||
.nav-pill{
|
||
flex:1 1 0%;
|
||
min-width:0;
|
||
display:flex;
|
||
align-items:center;
|
||
gap:3px;
|
||
background:#ffffff;
|
||
border-radius:9999px;
|
||
padding:8px 9px;
|
||
box-shadow:
|
||
0 10px 32px rgba(0,0,0,.10),
|
||
0 1px 4px rgba(0,0,0,.05),
|
||
inset 0 0 0 1px rgba(0,0,0,.035);
|
||
overflow-x:auto;
|
||
scrollbar-width:none;
|
||
}
|
||
.nav-pill::-webkit-scrollbar{display:none}
|
||
|
||
/* ——— nav buttons — heavily styled ——— */
|
||
.nav-btn{
|
||
appearance:none;
|
||
-webkit-appearance:none;
|
||
border:2px solid transparent;
|
||
outline:0;
|
||
margin:0;
|
||
background:transparent;
|
||
color:#6b6b74;
|
||
cursor:pointer;
|
||
display:inline-flex;
|
||
align-items:center;
|
||
justify-content:center;
|
||
gap:0;
|
||
padding:12px 13px;
|
||
border-radius:9999px;
|
||
flex-shrink:0;
|
||
white-space:nowrap;
|
||
font-weight:550;
|
||
font-size:15px;
|
||
line-height:1;
|
||
letter-spacing:-.15px;
|
||
transition:
|
||
background .20s cubic-bezier(.22,1,.36,1),
|
||
color .18s ease,
|
||
padding .24s cubic-bezier(.22,1,.36,1),
|
||
transform .12s ease,
|
||
box-shadow .18s ease,
|
||
border-color .15s;
|
||
position:relative;
|
||
user-select:none;
|
||
}
|
||
.nav-btn:hover{ background:#f4f4f6; color:#3a3a44 }
|
||
.nav-btn:active{ transform:scale(.95) }
|
||
.nav-btn:focus-visible{
|
||
border-color:#7adfff;
|
||
box-shadow:0 0 0 3px rgba(80,210,255,.28);
|
||
}
|
||
|
||
/* ACTIVE – unmistakable background color */
|
||
.nav-btn.is-active{
|
||
background:#e8e8ea;
|
||
color:#111217;
|
||
padding-left:17px;
|
||
padding-right:18px;
|
||
gap:9px;
|
||
box-shadow:
|
||
inset 0 1px 2px rgba(0,0,0,.035),
|
||
inset 0 0 0 1px rgba(0,0,0,.03);
|
||
}
|
||
.nav-btn.is-active:hover{ background:#e2e2e5 }
|
||
|
||
.nav-btn svg{
|
||
width:23px;height:23px;
|
||
display:block;flex:0 0 23px;
|
||
transition:transform .18s ease;
|
||
}
|
||
.nav-btn.is-active svg{ transform:scale(1.04) }
|
||
|
||
/* label expand / collapse */
|
||
.nav-label{
|
||
display:inline-block;
|
||
max-width:0;
|
||
opacity:0;
|
||
overflow:hidden;
|
||
transform:translateX(-6px);
|
||
white-space:nowrap;
|
||
font-weight:640;
|
||
font-size:15px;
|
||
transition:
|
||
max-width .34s cubic-bezier(.22,1,.36,1),
|
||
opacity .22s ease,
|
||
transform .28s cubic-bezier(.22,1,.36,1),
|
||
margin .22s ease;
|
||
}
|
||
.nav-btn.is-active .nav-label{
|
||
max-width:110px;
|
||
opacity:1;
|
||
transform:translateX(0);
|
||
margin-left:2px;
|
||
}
|
||
|
||
/* icon fill swap – outline → filled on active */
|
||
.ico-fill{display:none}
|
||
.nav-btn.is-active .ico-stroke{display:none}
|
||
.navbtn.is-active .ico-stroke, .nav-btn.is-active .ico-stroke{display:none !important}
|
||
.navbtn.is-active .ico-fill, .nav-btn.is-active .ico-fill{display:block !important}
|
||
|
||
/* FAB – SEPARATE – SAME ROW – HEAVILY STYLED */
|
||
.fab-btn{
|
||
flex:0 0 62px;
|
||
width:62px;
|
||
height:62px;
|
||
border-radius:50%;
|
||
border:0;
|
||
margin:0;
|
||
background:radial-gradient(circle at 32% 28%, #23232a 0%, #0f0f14 72%);
|
||
color:#fff;
|
||
display:grid;
|
||
place-items:center;
|
||
cursor:pointer;
|
||
box-shadow:
|
||
0 14px 34px rgba(0,0,0,.24),
|
||
0 3px 10px rgba(0,0,0,.12),
|
||
inset 0 1px 1px rgba(255,255,255,.09),
|
||
inset 0 -1px 2px rgba(0,0,0,.35);
|
||
transition: transform .16s ease, box-shadow .16s ease, background .15s, filter .15s;
|
||
flex-shrink:0;
|
||
position:relative;
|
||
z-index:2;
|
||
-webkit-tap-highlight-color:transparent;
|
||
}
|
||
.fab-btn:hover{
|
||
transform:translateY(-2px) scale(1.035);
|
||
box-shadow:0 18px 44px rgba(0,0,0,.28), 0 3px 10px rgba(0,0,0,.14), inset 0 1px 1px rgba(255,255,255,.09);
|
||
background:radial-gradient(circle at 32% 28%, #282832, #14141c 72%);
|
||
}
|
||
.fab-btn:active{ transform:scale(.93); filter:brightness(.96) }
|
||
.fab-btn:focus-visible{ outline:none; box-shadow:0 0 0 3px rgba(90,220,255,.35), 0 12px 30px rgba(0,0,0,.22) }
|
||
.fab-btn svg{ width:28px; height:28px; stroke-width:2.3; pointer-events:none }
|
||
|
||
/* hide nav+fabs when sheet open – sheet covers them */
|
||
.navrow.sheet-open .nav-pill,
|
||
.navrow.sheet-open .fab-btn{
|
||
opacity:0;
|
||
transform:translateY(10px) scale(.97);
|
||
filter:blur(3px);
|
||
pointer-events:none;
|
||
transition:opacity .22s ease, transform .22s ease, filter .22s ease;
|
||
}
|
||
|
||
/* narrow screens – keep 4 icons + FAB on ONE row */
|
||
@media (max-width:390px){
|
||
.navbtn{ padding:10px 11px }
|
||
.navbtn.is-active{ padding-left:14px; padding-right:15px }
|
||
.navbtn svg{width:21px;height:21px}
|
||
.fab-btn{ width:56px !important; height:56px !important; flex:0 0 56px !important }
|
||
.navrow{ gap:10px !important }
|
||
.nav-pill{ padding:6px 7px }
|
||
}
|
||
@media (max-width:350px){
|
||
.navb, .navbtn{ padding:9px 9px }
|
||
.fab-btn{ width:52px !important; height:52px !important; flex:0 0 52px !important }
|
||
}
|
||
|
||
/* =====================================================
|
||
2) PULL CARD — covers bottom — opens via + button
|
||
3) PULL HANDLE — pull up reveals 3-tab bar
|
||
tabs HIDDEN until pull-up
|
||
===================================================== */
|
||
.sheet-backdrop{
|
||
position:absolute;
|
||
inset:0;
|
||
background:rgba(18,18,24,.32);
|
||
backdrop-filter:blur(5px);
|
||
-webkit-backdrop-filter:blur(5px);
|
||
opacity:0;
|
||
pointer-events:none;
|
||
transition:opacity .26s ease;
|
||
z-index:40;
|
||
border-radius:inherit;
|
||
}
|
||
.sheet-backdrop.open{ opacity:1; pointer-events:auto }
|
||
|
||
/* sheet – centered, left:12px right:12px – NO translateX bug */
|
||
.sheet-card{
|
||
position:absolute;
|
||
left:12px;
|
||
right:12px;
|
||
bottom:12px;
|
||
background:#e9e9ee;
|
||
border-radius:26px;
|
||
z-index:50;
|
||
box-shadow:
|
||
0 -14px 50px rgba(0,0,0,.16),
|
||
0 -2px 12px rgba(0,0,0,.06),
|
||
inset 0 1px 0 rgba(255,255,255,.92);
|
||
/* closed – below screen */
|
||
transform:translateY(calc(100% + 30px));
|
||
opacity:0;
|
||
pointer-events:none;
|
||
transition:transform .38s cubic-bezier(.22,1,.36,1), opacity .28s ease;
|
||
display:flex;
|
||
flex-direction:column;
|
||
overflow:hidden;
|
||
touch-action:none;
|
||
max-height:calc(100% - 28px);
|
||
}
|
||
.sheet-card.open{
|
||
transform:translateY(0);
|
||
opacity:1;
|
||
pointer-events:auto;
|
||
}
|
||
.sheet-card.peek{ height:420px; }
|
||
.sheet-card.expanded{ height:650px; }
|
||
.sheet-card.dragging{ transition:none !important }
|
||
|
||
/* drag handle – 68px tall */
|
||
.sheet-handle{
|
||
flex:0 0 auto;
|
||
padding:18px 0 10px;
|
||
display:flex;
|
||
flex-direction:column;
|
||
align-items:center;
|
||
gap:7px;
|
||
cursor:grab;
|
||
touch-action:none;
|
||
user-select:none;
|
||
-webkit-user-select:none;
|
||
position:relative;
|
||
background:linear-gradient(180deg, rgba(255,255,255,.55), transparent 75%);
|
||
}
|
||
.sheet-handle:active{ cursor:grabbing }
|
||
.grab-pill{
|
||
width:44px; height:5px;
|
||
border-radius:99px;
|
||
background:#c9c9cf;
|
||
transition:width .15s ease, background .15s ease;
|
||
box-shadow:inset 0 1px 1px rgba(0,0,0,.06);
|
||
}
|
||
.sheet-card.dragging .grab-pill,
|
||
.sheet-handle:hover .grab-pill{ width:60px; background:#b5b5bc }
|
||
.drag-hint{
|
||
font-size:12px;
|
||
font-weight:600;
|
||
color:#9a9aa2;
|
||
letter-spacing:.01em;
|
||
transition:opacity .18s;
|
||
}
|
||
.sheet-card.dragging .drag-hint{ opacity:.55 }
|
||
|
||
/* close X – inside sheet */
|
||
.sheet-close{
|
||
position:absolute;
|
||
top:13px; right:13px;
|
||
width:36px; height:36px;
|
||
border-radius:50%;
|
||
border:0;
|
||
background:rgba(0,0,0,.045);
|
||
color:#7a7a82;
|
||
cursor:pointer;
|
||
display:grid;
|
||
place-items:center;
|
||
z-index:10;
|
||
transition: background .15s, transform .1s, color .15s;
|
||
font-size:17px; font-weight:600;
|
||
}
|
||
.sheet-close:hover{ background:rgba(0,0,0,.09); color:#333 }
|
||
.sheet-close:active{ transform:scale(.92) }
|
||
|
||
/* ===== 3) TABS – HIDDEN UNTIL PULL-UP ===== */
|
||
.sheet-tabs{
|
||
margin:0 16px;
|
||
background:#d9d9df;
|
||
border-radius:13px;
|
||
padding:4px;
|
||
display:flex;
|
||
gap:4px;
|
||
/* hidden in peek */
|
||
max-height:0;
|
||
opacity:0;
|
||
transform:translateY(-8px);
|
||
overflow:hidden;
|
||
pointer-events:none;
|
||
transition:
|
||
max-height .34s cubic-bezier(.22,1,.36,1),
|
||
opacity .24s ease,
|
||
transform .28s ease,
|
||
margin .24s ease;
|
||
}
|
||
.sheet-card.expanded .sheet-tabs{
|
||
max-height:56px;
|
||
opacity:1;
|
||
transform:translateY(0);
|
||
pointer-events:auto;
|
||
margin-bottom:10px;
|
||
}
|
||
.tab-btn{
|
||
flex:1;
|
||
border:2px solid transparent;
|
||
background:transparent;
|
||
padding:10px 8px;
|
||
border-radius:9px;
|
||
font:650 13.8px/1.15 "Inter", system-ui, sans-serif;
|
||
color:#6f6f77;
|
||
cursor:pointer;
|
||
text-align:center;
|
||
transition: background .16s ease, color .16s ease, box-shadow .16s ease, transform .1s ease, border-color .15s;
|
||
user-select:none;
|
||
white-space:nowrap;
|
||
}
|
||
.tab-btn:hover{ color:#3a3a42 }
|
||
.tab-btn:active{ transform:scale(.97) }
|
||
.tab-btn:focus-visible{ outline:none; border-color:#7adff5; box-shadow:0 0 0 3px rgba(90,220,255,.28) }
|
||
.tab-btn.active{
|
||
background:#ffffff;
|
||
color:#18181d;
|
||
box-shadow:0 2px 8px rgba(0,0,0,.07), 0 1px 2px rgba(0,0,0,.05);
|
||
}
|
||
|
||
/* sheet body */
|
||
.sheet-body{
|
||
flex:1;
|
||
overflow:hidden;
|
||
padding:0 18px 20px;
|
||
position:relative;
|
||
}
|
||
.sheet-card.expanded .sheet-body{
|
||
overflow-y:auto;
|
||
-webkit-overflow-scrolling:touch;
|
||
overscroll-behavior:contain;
|
||
}
|
||
.sheet-body::-webkit-scrollbar{width:0}
|
||
|
||
/* tab panels */
|
||
.tab-panel{ display:none }
|
||
.tab-panel.active{ display:block; animation:tabIn .24s cubic-bezier(.22,1,.36,1) }
|
||
@keyframes tabIn{ from{ opacity:0; transform:translateY(7px) } to{ opacity:1; transform:translateY(0) } }
|
||
|
||
/* ===== 4a) 4-GRID MENU BUTTONS – default sheet content ===== */
|
||
.menu-grid{
|
||
display:grid;
|
||
grid-template-columns:1fr 1fr;
|
||
gap:12px;
|
||
padding-top:4px;
|
||
}
|
||
.menu-card{
|
||
appearance:none;
|
||
-webkit-appearance:none;
|
||
border:1.5px solid #e5e5e8;
|
||
background:#ffffff;
|
||
border-radius:20px;
|
||
padding:20px 14px 16px;
|
||
text-align:center;
|
||
cursor:pointer;
|
||
color:#2a2a32;
|
||
font-family:inherit;
|
||
box-shadow:0 4px 16px rgba(0,0,0,.045), 0 1px 2px rgba(0,0,0,.03);
|
||
transition:
|
||
transform .14s ease,
|
||
box-shadow .18s ease,
|
||
background .15s ease,
|
||
border-color .15s ease;
|
||
user-select:none;
|
||
}
|
||
.menu-card:hover{
|
||
transform:translateY(-2px);
|
||
box-shadow:0 10px 28px rgba(0,0,0,.09), 0 2px 6px rgba(0,0,0,.04);
|
||
background:#fcfcfe;
|
||
border-color:#d8d8de;
|
||
}
|
||
.menu-card:active{ transform:translateY(0) scale(.97) }
|
||
.menu-card:focus-visible{
|
||
outline:none;
|
||
border-color:#6adff5;
|
||
box-shadow:0 0 0 3px rgba(80,210,255,.28), 0 4px 16px rgba(0,0,0,.06);
|
||
}
|
||
.menu-card .mi{
|
||
width:36px;height:36px;
|
||
margin:0 auto 10px;
|
||
display:grid;place-items:center;
|
||
border-radius:12px;
|
||
background:#f3f3f6;
|
||
transition:background .15s;
|
||
}
|
||
.menu-card:hover .mi{ background:#ececf0 }
|
||
.menu-card strong{
|
||
display:block;
|
||
font-size:15px;
|
||
font-weight:680;
|
||
letter-spacing:-.2px;
|
||
margin-bottom:3px;
|
||
}
|
||
.menu-card span{
|
||
font-size:12.5px;
|
||
color:#8a8a92;
|
||
font-weight:500;
|
||
}
|
||
|
||
/* profile */
|
||
.profile-top{display:flex;align-items:center;gap:14px;margin:4px 0 16px}
|
||
.p-avatar{
|
||
width:68px;height:68px;border-radius:20px;
|
||
background:linear-gradient(145deg,#6d3f26,#2b1710);
|
||
display:grid;place-items:center;
|
||
color:#f7ddd0;font-weight:750;font-size:22px;
|
||
flex:0 0 68px;
|
||
box-shadow:0 6px 20px rgba(90,45,20,.18);
|
||
position:relative;overflow:hidden;
|
||
}
|
||
.p-avatar::after{content:"";position:absolute;inset:0;background:radial-gradient(55px 36px at 30% 28%, rgba(255,220,190,.22), transparent 70%)}
|
||
.profile-stats{display:grid;grid-template-columns:repeat(3,1fr);gap:9px;margin:12px 0 16px}
|
||
.stat-card{
|
||
background:#fff;border-radius:16px;padding:13px 8px;text-align:center;
|
||
box-shadow:0 1px 4px rgba(0,0,0,.045);
|
||
border:1px solid #ececee;
|
||
}
|
||
.stat-card b{display:block;font-size:18.5px;font-weight:760;letter-spacing:-.2px}
|
||
.stat-card i{font-style:normal;font-size:11.8px;color:#8a8a92;font-weight:500}
|
||
|
||
/* profile list – heavily styled buttons */
|
||
.plist{display:flex;flex-direction:column;gap:9px}
|
||
.plist-btn{
|
||
appearance:none;-webkit-appearance:none;
|
||
width:100%;
|
||
text-align:left;
|
||
background:#ffffff;
|
||
border:1px solid #e6e6ea;
|
||
border-radius:15px;
|
||
padding:14px 15px;
|
||
font:520 14.8px/1.35 inherit;
|
||
color:#22222a;
|
||
cursor:pointer;
|
||
display:flex;
|
||
align-items:center;
|
||
justify-content:space-between;
|
||
box-shadow:0 1px 3px rgba(0,0,0,.035);
|
||
transition:background .14s ease, transform .1s ease, border-color .15s ease, box-shadow .15s ease;
|
||
}
|
||
.plist-btn:hover{ background:#f7f7f9; border-color:#dcdce2; transform:translateY(-1px); box-shadow:0 4px 14px rgba(0,0,0,.06) }
|
||
.plist-btn:active{ transform:translateY(0) scale(.985) }
|
||
.plist-btn:focus-visible{ outline:none; border-color:#6adff5; box-shadow:0 0 0 3px rgba(80,210,255,.25), 0 1px 4px rgba(0,0,0,.05) }
|
||
.plist-btn em{ font-style:normal; color:#a9a9b3; font-size:13px; font-weight:500 }
|
||
.plist-btn.danger{
|
||
color:#d92d20;
|
||
border-color:#f2c7c5;
|
||
background:#fffafa;
|
||
}
|
||
.plist-btn.danger:hover{ background:#fff0ef; border-color:#ebb4b1 }
|
||
|
||
/* preferences – sheet1.png match */
|
||
.pref-head{
|
||
display:flex;align-items:center;justify-content:space-between;
|
||
margin-bottom:6px;padding-right:4px;
|
||
}
|
||
.pref-title{ font-size:21px; font-weight:650; color:#6f6f76; letter-spacing:-.15px }
|
||
.pref-tune{
|
||
color:#a6a6ae; background:transparent; border:0; cursor:pointer;
|
||
padding:6px; border-radius:10px; transition:background .15s;
|
||
font-size:20px; line-height:1;
|
||
}
|
||
.pref-tune:hover{ background:#dedee3; color:#555 }
|
||
|
||
.theme3{ display:grid; grid-template-columns:repeat(3,1fr); gap:10px; margin-bottom:18px }
|
||
.theme-opt{
|
||
appearance:none; -webkit-appearance:none;
|
||
border:2px solid transparent;
|
||
background:#f1f1f4;
|
||
border-radius:20px;
|
||
padding:20px 10px 16px;
|
||
text-align:center;
|
||
cursor:pointer;
|
||
color:#8e8e96;
|
||
font-family:inherit;
|
||
transition: all .18s ease;
|
||
user-select:none;
|
||
}
|
||
.theme-opt:hover{ background:#e6e6ea; color:#5a5a62; transform:translateY(-1px) }
|
||
.theme-opt:active{ transform:translateY(0) scale(.97) }
|
||
.theme-opt:focus-visible{ outline:none; border-color:#6adff5; box-shadow:0 0 0 3px rgba(80,210,255,.25) }
|
||
.theme-opt.active{
|
||
background:#ffffff;
|
||
color:#18181c;
|
||
border-color:#ececec;
|
||
box-shadow:0 6px 22px rgba(0,0,0,.07), 0 1px 3px rgba(0,0,0,.04);
|
||
transform:translateY(-1px);
|
||
}
|
||
.theme-opt .ti{ font-size:28px; line-height:1; margin-bottom:10px; display:block }
|
||
.theme-opt .tn{ font-size:17.5px; font-weight:550; letter-spacing:-.2px }
|
||
|
||
/* toggle rows – iOS style – heavily styled */
|
||
.pref-list{ display:flex; flex-direction:column; gap:22px; margin-top:6px }
|
||
.pref-row{
|
||
display:flex; align-items:center; justify-content:space-between; gap:16px;
|
||
padding:3px 2px;
|
||
border-radius:12px;
|
||
transition:background .14s;
|
||
cursor:pointer;
|
||
}
|
||
.pref-row:hover{ background:rgba(0,0,0,.018) }
|
||
.pref-row b{ display:block; font-size:20px; font-weight:720; letter-spacing:-.25px; color:#1b1b20; margin-bottom:2px }
|
||
.pref-row span{ color:#9a9aa1; font-size:14.8px; font-weight:450; line-height:1.35 }
|
||
|
||
/* toggle switch – prominent */
|
||
.sw{
|
||
appearance:none; -webkit-appearance:none;
|
||
width:60px; height:32px;
|
||
flex:0 0 60px;
|
||
background:#d1d1d6;
|
||
border-radius:999px;
|
||
position:relative;
|
||
cursor:pointer;
|
||
transition: background .22s ease, box-shadow .2s;
|
||
outline:none;
|
||
border:2px solid transparent;
|
||
box-shadow: inset 0 1px 3px rgba(0,0,0,.1);
|
||
}
|
||
.sw::after{
|
||
content:"";
|
||
position:absolute;
|
||
top:2px; left:2px;
|
||
width:26px; height:26px;
|
||
background:linear-gradient(180deg,#fff,#f5f5f7);
|
||
border-radius:50%;
|
||
box-shadow:0 2px 5px rgba(0,0,0,.14), 0 0 0 1px rgba(0,0,0,.04);
|
||
transition: transform .26s cubic-bezier(.22,1,.36,1), width .15s;
|
||
}
|
||
.sw:checked{
|
||
background:linear-gradient(135deg,#3be9f5 0%, #2dd4bf 55%, #2ad8e0 100%);
|
||
box-shadow: inset 0 1px 3px rgba(0,120,120,.12), 0 0 0 1px rgba(45,212,191,.18);
|
||
}
|
||
.sw:checked::after{ transform:translateX(28px) }
|
||
.sw:focus-visible{ border-color:#6adff5; box-shadow:0 0 0 3px rgba(58,239,245,.30), inset 0 1px 3px rgba(0,0,0,.09) }
|
||
.sw:active::after{ width:29px }
|
||
|
||
/* pull-up CTA */
|
||
.pull-cta{
|
||
display:block;width:100%;
|
||
margin:2px 0 14px;
|
||
background:linear-gradient(180deg,#f7f7f9,#f0f0f3);
|
||
border:1.5px dashed #c8c8ce;
|
||
border-radius:14px;
|
||
padding:12px 14px;
|
||
text-align:center;
|
||
font:650 13.5px/1.4 inherit;
|
||
color:#5a5a62;
|
||
cursor:pointer;
|
||
transition: background .15s, transform .1s, border-color .15s, color .15s;
|
||
user-select:none;
|
||
}
|
||
.pull-cta:hover{ background:#efeff3; border-color:#b8b8be; color:#3a3a42 }
|
||
.pull-cta:active{ transform:scale(.97) }
|
||
.sheet.expanded .pull-cta{ display:none }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="app" id="app">
|
||
<div class="notch" aria-hidden="true"></div>
|
||
<div style="height:50px;display:flex;align-items:flex-end;justify-content:space-between;padding:0 20px 6px;font-size:14.5px;font-weight:600;color:#2a2a30"><span>9:41</span><span style="font-size:12.5px;opacity:.78">◴ ● ●</span></div>
|
||
|
||
<main class="feed" style="flex:1;overflow-y:auto;padding:8px 18px 130px;-webkit-overflow-scrolling:touch">
|
||
<!-- CNET / WWDC card – nav2.png -->
|
||
<div style="display:flex;justify-content:space-between;align-items:center;margin:4px 2px 12px;font-size:14.8px;color:#8a8a92">
|
||
<span>Tech</span>
|
||
<span style="color:#e52b2b;font-weight:800;font-size:20px;letter-spacing:-.3px">CNET</span>
|
||
</div>
|
||
|
||
<div style="display:flex;gap:13px;align-items:flex-start">
|
||
<div style="width:108px;height:108px;flex:0 0 108px;border-radius:22px;background:#0f1015;position:relative;overflow:hidden;box-shadow:0 8px 22px rgba(0,0,0,.14)">
|
||
<div style="position:absolute;left:13px;top:15px;color:#b9b9c0;font-weight:800;font-size:26px;line-height:.92;opacity:.5;letter-spacing:-1px">WW<br>DC</div>
|
||
<div style="position:absolute;bottom:12px;left:13px;color:#fff;font-weight:800;font-size:36px;text-shadow:0 0 14px rgba(255,255,255,.9);letter-spacing:-1.5px">26</div>
|
||
</div>
|
||
<div style="min-width:0;flex:1">
|
||
<h1>WWDC 2026: News on Siri, iOS 27, Tim Cook and More (Live)</h1>
|
||
<div style="display:flex;align-items:center;gap:9px;margin-top:10px;font-size:13px;color:#6f6f76">
|
||
<div style="width:30px;height:30px;border-radius:50%;background:linear-gradient(140deg,#705035,#281610);flex:0 0 30px"></div>
|
||
<div><strong style="color:#2b2b32">David Lumb</strong> • June 7, 2026 10:06 p.m</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="color:#8d8d95;font-size:13.6px;line-height:1.55;margin-top:16px">
|
||
<p style="margin-bottom:9px">Live from Apple Park — iOS 27 Liquid Glass, on-device Siri 2.0.</p>
|
||
<p style="color:#9a9aa1;font-size:12.6px;line-height:1.55">
|
||
<b style="color:#5a5a62">4-icon pill</b> Home • Explore • Saved • Reading — <b style="color:#5a5a62">FAB separate, same row</b><br>
|
||
Tap any icon → active pill background <b>#e8e8ea</b> moves, label expands, icon fills.<br>
|
||
Tap <b>+</b> → pull-card covers nav.<br>
|
||
Pull handle up → 3 tabs appear: Menu • Profile • Preferences.
|
||
</p>
|
||
</div>
|
||
</main>
|
||
|
||
<!-- =========================================================
|
||
1) BOTTOM NAV — nav1.png / nav2.png
|
||
4 ICONS — PILL + SEPARATE FAB — SAME ROW
|
||
========================================================= -->
|
||
<div style="position:absolute;left:12px;right:12px;bottom:20px;z-index:30" id="navDockWrap">
|
||
<!-- FLEX ROW – !important – never wraps – guarantees FAB stays right of pill -->
|
||
<div id="navRow"
|
||
style="display:flex;flex-direction:row;flex-wrap:nowrap;align-items:center;justify-content:space-between;gap:12px;width:100%">
|
||
|
||
<!-- PILL — 4 ICONS -->
|
||
<nav aria-label="Primary navigation"
|
||
style="flex:1 1 0%;min-width:0;display:flex;align-items:center;gap:3px;background:#ffffff;border-radius:9999px;padding:8px 8px;box-shadow:0 10px 30px rgba(0,0,0,.10),0 1px 3px rgba(0,0,0,.05),inset 0 0 0 1px rgba(0,0,0,.035);overflow-x:auto;scrollbar-width:none"
|
||
id="pillNav">
|
||
|
||
<!-- 1 — HOME — active – matches nav1.png / nav2.png left -->
|
||
<button type="button" class="navb nav-btn is-active" data-nav="home" aria-current="page"
|
||
style="border:2px solid transparent;outline:none;margin:0;background:#e8e8ea;color:#111217;cursor:pointer;display:inline-flex;align-items:center;gap:8px;padding:12px 16px 12px 16px;border-radius:9999px;flex-shrink:0;white-space:nowrap;font:600 15px/1 inherit;letter-spacing:-.15px;transition:all .2s cubic-bezier(.22,1,.36,1);box-shadow:inset 0 1px 2px rgba(0,0,0,.035)">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" style="flex:0 0 22px"><path d="M11.45 3.25a1.6 1.6 0 0 1 1.1 0l7.9 4.8a1.5 1.5 0 0 1 .8 1.35V19a2 2 0 0 1-2 2H4.75a2 2 0 0 1-2-2V9.35c0-.56.32-1.1.82-1.37l8.03-4.73Z"/><rect x="9.2" y="13.6" width="5.6" height="1.9" rx=".95" fill="#f2f2f4"/></svg>
|
||
<span style="font-weight:650">Home</span>
|
||
</button>
|
||
|
||
<!-- 2 — EXPLORE / decider — nav1.png middle, nav2.png second -->
|
||
<button class="navb nav-btn" type="button" aria-label="Explore" data-nav="explore"
|
||
style="border:2px solid transparent;outline:0;margin:0;background:transparent;color:#5b5b66;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:12px 13px;border-radius:9999px;flex-shrink:0;transition:all .18s">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.95" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M15.4 8.6l-4.6 2.1-2.1 4.7 4.6-2.1 2.1-4.6z"/></svg>
|
||
<span style="max-width:0;opacity:0;overflow:hidden;white-space:nowrap;font-weight:600">Explore</span>
|
||
</button>
|
||
|
||
<!-- 3 — SAVED / bookmark — nav1.png right, nav2.png third -->
|
||
<button class="navb nav-btn" type="button" aria-label="Saved" data-nav="saved"
|
||
style="border:2px solid transparent;outline:0;margin:0;background:transparent;color:#5b5b66;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:12px 13px;border-radius:9999px;flex-shrink:0;transition:all .18s;white-space:nowrap">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.7h10a1.3 1.3 0 0 1 1.3 1.3v15.4l-6.3-3.9-6.3 3.9V5A1.3 1.3 0 0 1 7 3.7Z"/><path d="M10 9.3h4"/></svg>
|
||
<span style="max-width:0;opacity:0;overflow:hidden;white-space:nowrap;font-weight:600">Saved</span>
|
||
</button>
|
||
|
||
<!-- 4 — READING / book-open — nav2.png fourth -->
|
||
<button class="navb nav-btn" type="button" aria-label="Reading" data-nav="reading"
|
||
style="border:2px solid transparent;outline:0;margin:0;background:transparent;color:#5b5b66;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:12px 13px;border-radius:9999px;flex-shrink:0;white-space:nowrap;font:500 15px/1 inherit;transition:all .18s">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.85" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4.4 5.5C4.4 4.35 5.45 3.5 6.6 3.75l3.7.78A2.7 2.7 0 0 1 12.6 7.1v13.2a2.3 2.3 0 0 0-2.15-1.25H5.9A1.7 1.7 0 0 1 4.2 17.35V5.5Z"/><path d="M12.6 7v13.4c.65-.25 1.4-.25 2.05-.07l3.5.72c1.15.25 2.15-.6 2.15-1.75V7.2"/></svg>
|
||
<span style="max-width:0;opacity:0;overflow:hidden;white-space:nowrap;font-weight:600">Reading</span>
|
||
</button>
|
||
</nav>
|
||
|
||
<!-- FAB — SEPARATE — SAME ROW — RIGHT -->
|
||
<button id="fabBtn" type="button" aria-expanded="false" aria-label="Open menu sheet"
|
||
style="flex:0 0 60px;width:60px;height:60px;border-radius:50%;border:0;margin:0;background:radial-gradient(circle at 32% 28%,#23232c,#0f0f14 70%);color:#fff;display:grid;place-items:center;cursor:pointer;flex-shrink:0;box-shadow:0 12px 30px rgba(0,0,0,.24),0 2px 8px rgba(0,0,0,.12),inset 0 1px 1px rgba(255,255,255,.07);transition:transform .15s,box-shadow .16s;z-index:2;-webkit-tap-highlight-color:transparent">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" width="26" height="26" aria-hidden="true"><path d="M12 5.8v12.4M5.8 12h12.4"/></svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== 2) PULL CARD — covers bottom — opens via + button ===== -->
|
||
<!-- backdrop – sits ABOVE nav (z 40 > nav 30), BELOW sheet (z 50) -->
|
||
<div id="sheetBackdrop" aria-hidden="true"
|
||
style="position:absolute;inset:0;background:rgba(18,18,24,.32);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);opacity:0;pointer-events:none;transition:opacity .26s ease;z-index:40;border-radius:inherit"></div>
|
||
|
||
<!-- sheet – ALWAYS centered, NEVER cut off left -->
|
||
<div id="sheet" role="dialog" aria-modal="true" aria-hidden="true"
|
||
style="position:absolute;left:10px;right:10px;bottom:10px;background:#e9e9ee;border-radius:26px;z-index:50;box-shadow:0 -14px 50px rgba(0,0,0,.18),inset 0 1px 0 rgba(255,255,255,.9);transform:translateY(calc(100% + 28px));opacity:0;pointer-events:none;transition:transform .36s cubic-bezier(.22,1,.36,1),opacity .28s;display:flex;flex-direction:column;overflow:hidden;touch-action:none;max-height:calc(100% - 28px)">
|
||
|
||
<!-- 3) PULL HANDLE — pull up to reveal tabs -->
|
||
<div id="sheetHandle"
|
||
style="flex:0 0 auto;padding:18px 0 10px;display:flex;flex-direction:column;align-items:center;gap:7px;cursor:grab;touch-action:none;user-select:none;background:linear-gradient(180deg,rgba(255,255,255,.55),transparent)">
|
||
<span id="grabBar" style="display:block;width:44px;height:5px;border-radius:99px;background:#c9c9cf;transition:width .15s,background .15s;box-shadow:inset 0 1px 1px rgba(0,0,0,.06)"></span>
|
||
<div id="dragText" style="font-size:12px;font-weight:600;color:#9a9aa2;letter-spacing:.01em;transition:opacity .18s">pull up • swipe down to close</div>
|
||
<button id="sheetClose" aria-label="Close sheet"
|
||
style="position:absolute;top:12px;right:12px;width:34px;height:34px;border-radius:50%;border:0;background:rgba(0,0,0,.05);color:#777;cursor:pointer;display:grid;place-items:center;font-size:16px;font-weight:700">×</button>
|
||
</div>
|
||
|
||
<!-- 3) TABBED BAR — HIDDEN UNTIL PULL-UP -->
|
||
<div id="sheetTabs" role="tablist" aria-label="Sheet sections"
|
||
style="margin:0 16px;background:#d9d9df;border-radius:13px;padding:4px;display:flex;gap:4px;max-height:0;opacity:0;transform:translateY(-6px);overflow:hidden;pointer-events:none;transition:max-height .32s cubic-bezier(.22,1,.36,1),opacity .24s,transform .24s,margin .24s">
|
||
<button role="tab" aria-selected="true" data-stab="menu"
|
||
class="stab active"
|
||
style="flex:1;border:2px solid transparent;background:#fff;padding:10px;border-radius:9px;font:650 14px/1 inherit;color:#18181c;cursor:pointer;box-shadow:0 1px 5px rgba(0,0,0,.07);transition:all .15s">Menu</button>
|
||
<button role="tab" aria-selected="false" data-stab="profile"
|
||
class="stab"
|
||
style="flex:1;border:2px solid transparent;background:transparent;padding:10px;border-radius:9px;font:650 14.5px/1 inherit;color:#6f6f77;cursor:pointer;transition:all .15s">Profile</button>
|
||
<button role="tab" aria-selected="false" data-stab="prefs"
|
||
class="stab"
|
||
style="flex:1;border:2px solid transparent;background:transparent;padding:10px;border-radius:9px;font:650 14.5px/1 inherit;color:#6f6f77;cursor:pointer;transition:all .15s">Preferences</button>
|
||
</div>
|
||
|
||
<div id="sheetBody" style="flex:1;overflow:hidden;padding:0 18px 20px;position:relative">
|
||
|
||
<!-- 4a) 4-GRID MENU BUTTONS — DEFAULT when + clicked -->
|
||
<div id="tp-menu" class="tpan active">
|
||
<!-- pull-up CTA — heavily styled button -->
|
||
<button id="pullUpBtn" type="button"
|
||
style="display:block;width:100%;margin:2px 0 14px;background:linear-gradient(180deg,#f7f7f9,#f0f0f3);border:1.5px dashed #c8c8ce;border-radius:14px;padding:13px 14px;text-align:center;font:650 13.5px/1.4 inherit;color:#5a5a64;cursor:pointer;transition:background .15s,transform .1s,user-select:none">
|
||
↑ Pull up — reveal Profile / Preferences tabs
|
||
</button>
|
||
|
||
<div style="font-size:11.5px;font-weight:750;color:#8b8b93;text-transform:uppercase;letter-spacing:.09em;margin:2px 2px 12px">Quick actions</div>
|
||
<!-- 4-grid – styled buttons -->
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
|
||
<button type="button" class="menu-card" data-act="new"
|
||
style="appearance:none;border:1.5px solid #e6e6ea;background:#ffffff;border-radius:20px;padding:20px 14px 16px;text-align:center;cursor:pointer;color:#22222a;font-family:inherit;box-shadow:0 4px 16px rgba(0,0,0,.045),0 1px 2px rgba(0,0,0,.03);transition:transform .13s ease,box-shadow .18s ease,background .15s,border-color .15s">
|
||
<div style="width:38px;height:38px;margin:0 auto 10px;border-radius:12px;background:#f3f3f6;display:grid;place-items:center;font-size:20px;font-weight:700">+</div>
|
||
<strong style="display:block;font-size:15px;font-weight:680;letter-spacing:-.2px">New Post</strong>
|
||
<span style="font-size:12.5px;color:#8a8a92">Share update</span>
|
||
</button>
|
||
<button type="button" class="menu-card" data-act="explore"
|
||
style="appearance:none;border:1.5px solid #e6e6ea;background:#ffffff;border-radius:20px;padding:20px 14px 16px;text-align:center;cursor:pointer;color:#22222a;font-family:inherit;box-shadow:0 4px 16px rgba(0,0,0,.045),0 1px 2px rgba(0,0,0,.03);transition:transform .13s ease,box-shadow .18s ease,background .15s,border-color .15s">
|
||
<div style="width:38px;height:38px;margin:0 auto 10px;border-radius:12px;background:#f3f3f6;display:grid;place-items:center">🧭</div>
|
||
<strong style="display:block;font-size:15px;font-weight:680;letter-spacing:-.2px">Explore</strong>
|
||
<span style="font-size:12.5px;color:#8a8a92">Discover</span>
|
||
</button>
|
||
<button type="button" class="menu-card" data-act="saved"
|
||
style="appearance:none;border:1.5px solid #e6e6ea;background:#ffffff;border-radius:20px;padding:20px 14px 16px;text-align:center;cursor:pointer;color:#22222a;font-family:inherit;box-shadow:0 4px 16px rgba(0,0,0,.045),0 1px 2px rgba(0,0,0,.03);transition:transform .13s ease,box-shadow .18s ease,background .15s,border-color .15s">
|
||
<div style="width:38px;height:38px;margin:0 auto 10px;border-radius:12px;background:#f3f3f6;display:grid;place-items:center">🔖</div>
|
||
<strong style="display:block;font-size:15px;font-weight:680;letter-spacing:-.2px">Saved</strong>
|
||
<span style="font-size:12.5px;color:#8a8a92">Bookmarks</span>
|
||
</button>
|
||
<button type="button" class="menu-card" data-act="reading"
|
||
style="appearance:none;border:1.5px solid #e6e6ea;background:#ffffff;border-radius:20px;padding:20px 14px 16px;text-align:center;cursor:pointer;color:#22222a;font-family:inherit;box-shadow:0 4px 16px rgba(0,0,0,.045),0 1px 2px rgba(0,0,0,.03);transition:transform .13s ease,box-shadow .18s ease,background .15s,border-color .15s">
|
||
<div style="width:38px;height:38px;margin:0 auto 10px;border-radius:12px;background:#f3f3f6;display:grid;place-items:center">📖</div>
|
||
<strong style="display:block;font-size:15px;font-weight:680;letter-spacing:-.2px">Reading</strong>
|
||
<span style="font-size:12.5px;color:#8a8a92">Continue</span>
|
||
</button>
|
||
</div>
|
||
<p style="text-align:center;font-size:11.8px;color:#a9a9b2;margin-top:12px">Pull up for Profile & Preferences tabs →</p>
|
||
</div>
|
||
|
||
<!-- 4b) PROFILE TAB -->
|
||
<div id="tp-profile" class="tpan" hidden>
|
||
<div style="display:flex;gap:13px;align-items:center;margin:4px 0 14px">
|
||
<div style="width:62px;height:62px;border-radius:18px;background:linear-gradient(145deg,#6d3f26,#2b1710);display:grid;place-items:center;color:#f7ddd0;font-weight:750;font-size:20px;flex:0 0 62px;box-shadow:0 6px 18px rgba(90,45,20,.18)">DL</div>
|
||
<div>
|
||
<div style="font-size:20px;font-weight:760;letter-spacing:-.2px">David Lumb</div>
|
||
<div style="font-size:13px;color:#7b7b84;line-height:1.45;margin-top:2px">david.lumb@cnet.com<br>San Francisco • Tech Editor</div>
|
||
<span style="display:inline-block;margin-top:6px;background:#e6fbf6;color:#149985;font-size:11px;font-weight:700;padding:3px 9px;border-radius:999px">PRO • WWDC Live</span>
|
||
</div>
|
||
</div>
|
||
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin:10px 0 14px">
|
||
<div style="background:#fff;border-radius:15px;padding:12px 8px;text-align:center;box-shadow:0 1px 5px rgba(0,0,0,.045)"><b style="font-size:17px;font-weight:750;display:block">128</b><span style="font-size:11.5px;color:#8a8a92">Articles</span></div>
|
||
<div style="background:#fff;border-radius:15px;padding:12px 8px;text-align:center;box-shadow:0 1px 5px rgba(0,0,0,.045)"><b style="font-size:17px;font-weight:750;display:block">4.2k</b><span style="font-size:11.5px;color:#8a8a92">Followers</span></div>
|
||
<div style="background:#fff;border-radius:15px;padding:12px 8px;text-align:center;box-shadow:0 1px 5px rgba(0,0,0,.045)"><b style="font-size:17px;font-weight:750;display:block">27</b><span style="font-size:11.5px;color:#8a8a92">Following</span></div>
|
||
</div>
|
||
<div style="display:flex;flex-direction:column;gap:8px">
|
||
<button type="button" style="width:100%;text-align:left;background:#fff;border:1px solid #e6e6ea;border-radius:13px;padding:13px 14px;font:520 14.5px inherit;cursor:pointer;display:flex;justify-content:space-between;transition:background .13s">Edit profile <span style="color:#b0b0b6">›</span></button>
|
||
<button type="button" style="width:100%;text-align:left;background:#fff;border:1px solid #e6e6ea;border-radius:13px;padding:13px 14px;font:520 14.5px inherit;cursor:pointer;display:flex;justify-content:space-between">Notifications <span style="color:#39d6c5;font-weight:650;font-size:12.8px">On</span></button>
|
||
<button type="button" style="width:100%;text-align:left;background:#fff;border:1px solid #e6e6ea;border-radius:13px;padding:13px 14px;font:520 14.5px inherit;cursor:pointer;display:flex;justify-content:space-between">Reading history <span style="color:#a9a9b3;font-size:12.8px">142</span></button>
|
||
<button type="button" style="width:100%;text-align:left;background:#fffbfb;border:1px solid #f2c9c7;border-radius:13px;padding:13px 14px;font:600 14.5px inherit;color:#d92d20;cursor:pointer">Sign out</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 4c) PREFERENCES TAB — sheet1.png UI -->
|
||
<div id="tp-prefs" class="tpan" hidden>
|
||
<!-- header like sheet1.png -->
|
||
<div style="display:flex;align-items:center;justify-content:space-between;margin:2px 2px 14px;padding-right:4px">
|
||
<div style="font-size:20px;font-weight:550;color:#8f8f96;letter-spacing:-.15px">Preferences</div>
|
||
<button type="button" aria-label="Tune"
|
||
style="border:0;background:transparent;color:#a9a9b0;cursor:pointer;padding:5px;border-radius:9px;font-size:20px;line-height:1">⚙️</button>
|
||
</div>
|
||
|
||
<!-- theme selector – exactly sheet1.png: System / Dark / [Light white card] -->
|
||
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:20px">
|
||
<button type="button" class="theme-btn" data-theme="system"
|
||
style="border:0;background:transparent;border-radius:20px;padding:20px 10px 16px;text-align:center;color:#9a9aa0;cursor:pointer;transition:all .16s;font-family:inherit">
|
||
<div style="font-size:28px;margin-bottom:8px;opacity:.75">🖥️</div>
|
||
<div style="font-size:17px;font-weight:500">System</div>
|
||
</button>
|
||
<button type="button" class="theme-btn" data-theme="dark"
|
||
style="border:0;background:transparent;border-radius:20px;padding:20px 10px 16px;text-align:center;color:#9a9aa0;cursor:pointer;transition:all .16s;font-family:inherit">
|
||
<div style="font-size:28px;margin-bottom:8px;opacity:.75">🌙</div>
|
||
<div style="font-size:17px;font-weight:500">Dark</div>
|
||
</button>
|
||
<button type="button" class="theme-btn active" data-theme="light" aria-pressed="true"
|
||
style="border:0;background:#ffffff;border-radius:20px;padding:20px 10px 16px;text-align:center;color:#18181c;cursor:pointer;box-shadow:0 6px 22px rgba(0,0,0,.07),0 1px 3px rgba(0,0,0,.04);transition:all .18s;font-family:inherit">
|
||
<div style="font-size:28px;margin-bottom:8px">☀️</div>
|
||
<div style="font-size:17px;font-weight:600">Light</div>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Sound – ON – cyan toggle like sheet1.png -->
|
||
<div style="display:flex;align-items:center;justify-content:space-between;gap:16px;padding:6px 2px;margin-bottom:18px">
|
||
<div>
|
||
<div style="font-size:21px;font-weight:720;letter-spacing:-.25px">Sound</div>
|
||
<div style="color:#9a9aa1;font-size:15px;margin-top:2px">Enable sound effects</div>
|
||
</div>
|
||
<button type="button" class="tgl on" data-tog="sound" aria-pressed="true"
|
||
style="width:60px;height:34px;border-radius:999px;border:0;cursor:pointer;position:relative;flex:0 0 60px;background:linear-gradient(135deg,#3ae9f5,#2fd4bf);box-shadow:inset 0 1px 3px rgba(0,100,100,.12);transition:background .2s">
|
||
<span style="position:absolute;top:3px;left:30px;width:28px;height:28px;background:#f6f8fa;border-radius:50%;box-shadow:0 1px 5px rgba(0,0,0,.16);transition:left .24s cubic-bezier(.22,1,.36,1)"></span>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Compact mode – OFF – gray toggle -->
|
||
<div style="display:flex;align-items:center;justify-content:space-between;gap:16px;padding:6px 2px">
|
||
<div>
|
||
<div style="font-size:21px;font-weight:720;letter-spacing:-.25px">Compact mode</div>
|
||
<div style="color:#9a9aa1;font-size:15px;margin-top:2px">More content, less space</div>
|
||
</div>
|
||
<button type="button" class="tgl" data-tog="compact" aria-pressed="false" id="compactToggleBtn"
|
||
style="width:60px;height:34px;border-radius:999px;border:0;cursor:pointer;position:relative;flex:0 0 60px;background:#d1d1d6;box-shadow:inset 0 1px 3px rgba(0,0,0,.09);transition:background .22s">
|
||
<span style="position:absolute;top:3px;left:3px;width:28px;height:28px;background:#fff;border-radius:50%;box-shadow:0 1px 4px rgba(0,0,0,.16);transition:left .24s cubic-bezier(.22,1,.36,1)"></span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
/* ===== 4 ICON NAV — active background color swap ===== */
|
||
const navBtns = document.querySelectorAll('.navb, .nav-btn, [data-n]');
|
||
function activateNav(btn){
|
||
document.querySelectorAll('#pillNav button, .navb, .nav-btn').forEach(n=>{
|
||
n.classList.remove('active','is-active');
|
||
n.removeAttribute('aria-current');
|
||
// reset styles – STYLED BUTTONS
|
||
n.style.background='transparent';
|
||
n.style.color='#6b6b74';
|
||
const lb=n.querySelector('.nav-label, span:last-child');
|
||
if(lb && lb.textContent.trim().length>2){ lb.style.maxWidth='0'; lb.style.opacity='0'; lb.style.marginLeft='0'; }
|
||
// icon outline on / fill off
|
||
const s=n.querySelector('.ico-stroke'); const f=n.querySelector('.ico-fill');
|
||
if(s) s.style.display='';
|
||
if(f) f.style.display='none';
|
||
});
|
||
// activate clicked
|
||
btn.classList.add('active','is-active');
|
||
btn.setAttribute('aria-current','page');
|
||
btn.style.background='#e7e7ea';
|
||
btn.style.color='#111217';
|
||
const label=btn.querySelector('.nav-label') || btn.querySelector('span:last-child');
|
||
if(label && label.textContent.trim().length>2){ label.style.maxWidth='90px'; label.style.opacity='1'; label.style.marginLeft='6px' }
|
||
const fs=btn.querySelector('.ico-stroke'); const ff=btn.querySelector('.ico-fill');
|
||
if(fs) fs.style.display='none';
|
||
if(ff) ff.style.display='block';
|
||
}
|
||
document.querySelectorAll('#pillNav button').forEach(b=> b.addEventListener('click', ()=> activateNav(b)));
|
||
// ensure initial Home is styled active
|
||
activateNav(document.querySelector('#pillNav button[data-n="home"]')||document.querySelector('#pillNav .navb'));
|
||
|
||
/* ===== SHEET — pull card — covers bottom — opens via + =====
|
||
- sheet is position:absolute left:10px;right:10px;bottom:10px
|
||
→ centered, never cut off left – fixes “cut off vertically on left”
|
||
- z-index: sheet 50 > backdrop 35 > nav 30 → always ON TOP of navbar
|
||
- + button opens it
|
||
*/
|
||
const sheet = document.getElementById('sheet');
|
||
const backdrop = document.getElementById('sheetBackdrop');
|
||
const fabBtn = document.getElementById('fabBtn');
|
||
let sheetState = 'closed'; // closed | peek | expanded
|
||
|
||
function setSheetState(m){
|
||
sheetMode = sMode = m;
|
||
sheetMode = m; // alias for drag code
|
||
sMode = m;
|
||
const open = m !== 'closed';
|
||
// backdrop
|
||
backdrop.style.opacity = open ? '1' : '0';
|
||
backdrop.style.pointerEvents = open ? 'auto' : 'none';
|
||
// hide nav under sheet
|
||
const navRow = document.getElementById('navRow') || document.querySelector('.navrow') || document.querySelector('#bottomNav');
|
||
if(navRow){ navRow.style.opacity = open ? '0' : ''; navRow.style.transform = open ? 'translateY(8px)' : ''; navRow.style.filter = open ? 'blur(2px)' : ''; navRow.style.pointerEvents = open ? 'none' : '' }
|
||
// sheet transform – NO translateX – left/right anchor keeps it centered
|
||
if(!open){
|
||
sheet.style.transform='translateY(calc(100% + 30px))';
|
||
sheet.style.opacity='0';
|
||
sheet.style.pointerEvents='none';
|
||
sheet.setAttribute('aria-hidden','true');
|
||
fabBtn.setAttribute('aria-expanded','false');
|
||
return;
|
||
}
|
||
sheet.style.opacity='1';
|
||
sheet.style.pointerEvents='auto';
|
||
sheet.style.transform='translateY(0)';
|
||
sheet.setAttribute('aria-hidden','false');
|
||
fabBtn.setAttribute('aria-expanded','true');
|
||
|
||
// height + tabs visibility – TABS ONLY VISIBLE WHEN PULLED UP
|
||
const tabs = document.getElementById('sheetTabs');
|
||
const body = document.getElementById('sheetBody');
|
||
if(m==='expanded'){
|
||
sheet.style.height='610px';
|
||
if(tabs){ tabs.style.maxHeight='54px'; tabs.style.opacity='1'; tabs.style.transform='translateY(0)'; tabs.style.pointerEvents='auto'; tabs.style.marginBottom='10px' }
|
||
if(body) body.style.overflowY='auto';
|
||
} else { // peek
|
||
sheet.style.height='410px';
|
||
if(tabs){ tabs.style.maxHeight='0'; tabs.style.opacity='0'; tabs.style.transform='translateY(-6px)'; tabs.style.pointerEvents='none'; tabs.style.marginBottom='0' }
|
||
if(body) body.style.overflow='hidden';
|
||
}
|
||
}
|
||
let sheetMode = 'closed';
|
||
let sMode = 'closed';
|
||
function openSheet(m){ setSheet(m||'peek') }
|
||
function openPeek(){ setSheet('peek') }
|
||
function openExpanded(){ setSheet('expanded') }
|
||
function closeSheet(){ setSheet('closed') }
|
||
|
||
fabBtn.addEventListener('click', ()=> sheetMode==='closed' ? openSheet('peek') : closeSheet());
|
||
backdrop.addEventListener('click', closeSheet);
|
||
document.getElementById('sheetClose').addEventListener('click', closeSheet);
|
||
document.getElementById('pullUpBtn')?.addEventListener('click', openExpanded);
|
||
document.addEventListener('keydown', e=>{ if(e.key==='Escape' && sheetMode!=='closed') closeSheet() });
|
||
|
||
/* ===== 3-tab bar — only visible after pull-up =====
|
||
a) Menu — 4 grid buttons — DEFAULT
|
||
b) Profile
|
||
c) Preferences — sheet1.png UI
|
||
*/
|
||
function switchTab(which){
|
||
document.querySelectorAll('.stab').forEach(t=>{
|
||
const on = t.dataset.stab===which;
|
||
t.classList.toggle('active', on);
|
||
t.setAttribute('aria-selected', on);
|
||
t.style.background = on ? '#fff' : 'transparent';
|
||
t.style.color = on ? '#18181c' : '#6f6f77';
|
||
t.style.boxShadow = on ? '0 1px 5px rgba(0,0,0,.07)' : 'none';
|
||
});
|
||
const panels = {menu:'tp-menu', profile:'tp-profile', prefs:'tp-prefs'};
|
||
Object.entries(panels).forEach(([k,id])=>{
|
||
const p=document.getElementById(id);
|
||
if(!p) return;
|
||
const show = k===which;
|
||
p.classList.toggle('active', show);
|
||
p.hidden = !show;
|
||
p.style.display = show ? 'block' : 'none';
|
||
});
|
||
}
|
||
document.querySelectorAll('.stab').forEach(tab=>{
|
||
tab.addEventListener('click', ()=>{
|
||
const t = tab.dataset.stab;
|
||
if(sheetMode!=='expanded'){ openExpanded(); setTimeout(()=>switchTab(t), 120); return; }
|
||
switchTab(t);
|
||
});
|
||
});
|
||
// default to menu grid
|
||
switchTab('prefs'); // keep preferences as initial inner default? spec: 4a menu is default when + clicked
|
||
// actually per spec 4a: 4 grid menu buttons - default content when + clicked
|
||
// so override:
|
||
switchTab('menu');
|
||
document.querySelectorAll('.stab').forEach((t,i)=>{
|
||
const isMenu = t.dataset.stab==='menu';
|
||
t.classList.toggle('active', isMenu);
|
||
t.style.background = isMenu ? '#fff' : 'transparent';
|
||
t.style.color = isMenu ? '#18181c' : '#6f6f77';
|
||
});
|
||
|
||
/* theme buttons – styled – sheet1.png */
|
||
document.querySelectorAll('.theme-btn, .thb').forEach(b=>{
|
||
b.addEventListener('mouseenter', ()=> { if(!b.classList.contains('active')) b.style.background='#e8e8eb' });
|
||
b.addEventListener('mouseleave', ()=> { if(!b.classList.contains('active')) b.style.background='transparent' });
|
||
b.addEventListener('click', ()=>{
|
||
const g=b.closest('.theme3') || b.parentElement;
|
||
g.querySelectorAll('button').forEach(x=>{
|
||
x.classList.remove('active');
|
||
x.style.background='transparent';
|
||
x.style.color='#8b8b93';
|
||
x.style.boxShadow='none';
|
||
x.style.transform='none';
|
||
x.style.borderColor='transparent';
|
||
});
|
||
b.classList.add('active');
|
||
b.style.background='#ffffff';
|
||
b.style.color='#15151a';
|
||
b.style.boxShadow='0 6px 20px rgba(0,0,0,.06)';
|
||
b.style.transform='translateY(-1px)';
|
||
});
|
||
});
|
||
|
||
/* toggle switches – styled cyan / gray – like sheet1.png */
|
||
function bindToggle(btn){
|
||
if(!btn) return;
|
||
btn.addEventListener('click', ()=>{
|
||
const on = btn.getAttribute('aria-pressed') === 'true';
|
||
const next = !on;
|
||
btn.setAttribute('aria-pressed', String(next));
|
||
btn.classList.toggle('on', next);
|
||
// move knob
|
||
const knob = btn.querySelector('span');
|
||
if(knob){ knob.style.left = next ? '27px' : '3px'; knob.style.transform = next ? 'scale(1)' : 'scale(1)'; }
|
||
btn.style.background = next
|
||
? 'linear-gradient(135deg,#3be7f2,#2fd4be)'
|
||
: '#d1d1d6';
|
||
// haptics demo
|
||
if(navigator.vibrate && next) try{ navigator.vibrate(8) }catch(e){}
|
||
});
|
||
}
|
||
document.querySelectorAll('.tgl, [data-tog]').forEach(bindToggle);
|
||
// compact mode actually shrinks nav
|
||
document.querySelector('[data-tog="compact"]')?.addEventListener('click', function(){
|
||
setTimeout(()=>{
|
||
const on = this.getAttribute('aria-pressed')==='true';
|
||
document.querySelectorAll('.navb').forEach(n=>{
|
||
n.style.paddingTop = on ? '9px' : '';
|
||
n.style.paddingBottom = on ? '9px' : '';
|
||
});
|
||
}, 10);
|
||
});
|
||
|
||
/* menu grid card hover – rich button styling */
|
||
document.querySelectorAll('.menu-card').forEach(c=>{
|
||
c.addEventListener('mouseenter', ()=>{ c.style.transform='translateY(-2px)'; c.style.boxShadow='0 10px 28px rgba(0,0,0,.09)'; c.style.borderColor='#d8d8de' });
|
||
c.addEventListener('mouseleave', ()=>{ c.style.transform=''; c.style.boxShadow='0 4px 16px rgba(0,0,0,.045)'; c.style.borderColor='#e6e6ea' });
|
||
c.addEventListener('mousedown', ()=> c.style.transform='scale(.97)');
|
||
c.addEventListener('mouseup', ()=> c.style.transform='translateY(-2px)');
|
||
c.addEventListener('click', ()=>{
|
||
const act=c.dataset.act;
|
||
if(act==='explore') { closeSheet(); setTimeout(()=> document.querySelector('[data-n="explore"]')?.click(), 220) }
|
||
if(act==='saved') { closeSheet(); setTimeout(()=> document.querySelector('[data-n="saved"]')?.click(), 220) }
|
||
if(act==='reading') { closeSheet(); setTimeout(()=> document.querySelector('[data-n="reading"]')?.click(), 220) }
|
||
});
|
||
});
|
||
|
||
/* ===== PULL HANDLE — pull up shows tabs =====
|
||
sheet is left:10px right:10px — NO translateX — never cut off left
|
||
*/
|
||
let dragging=false, sy=0, cy=0, vy=0, lt=0, ly=0;
|
||
const handleEl = document.getElementById('sheetHandle');
|
||
|
||
function pd(y, e){
|
||
if(sheetMode==='closed') return;
|
||
dragging='maybe';
|
||
sy=cy=ly=y; lt=performance.now(); vy=0;
|
||
if(e && e.cancelable) e.preventDefault();
|
||
}
|
||
function pm(y, e){
|
||
if(!dragging) return;
|
||
const dy = y - sy;
|
||
if(dragging==='maybe' && Math.abs(dy) < 5){ return }
|
||
if(dragging==='maybe'){
|
||
dragging=true;
|
||
sheet.classList.add('dragging');
|
||
const gb=document.getElementById('grabBar'); if(gb){gb.style.width='60px';gb.style.background='#b5b5bc'}
|
||
}
|
||
const now=performance.now();
|
||
vy = (y - ly) / Math.max(1, now - lt);
|
||
ly=y; lt=now; cy=y;
|
||
// move sheet – NO translateX – safe, centered
|
||
let ty = dy * (dy < 0 ? 0.92 : 1);
|
||
sheet.style.transform = `translateY(${ty}px)`;
|
||
sheet.style.transition='none';
|
||
const ht=document.getElementById('dragText');
|
||
if(ht) ht.textContent = sheetMode==='peek' && dy < -14 ? 'release to expand ↑' : sheetMode==='expanded' && dy > 14 ? 'release to collapse ↓' : 'pull up • swipe down to close';
|
||
if(e && e.cancelable) e.preventDefault();
|
||
}
|
||
function pu(){
|
||
if(!dragging) return;
|
||
const was = dragging===true;
|
||
dragging=false;
|
||
sheet.classList.remove('dragging');
|
||
const dy = cy - sy;
|
||
sheet.style.transition='transform .28s cubic-bezier(.22,1,.36,1), opacity .22s ease';
|
||
if(was){
|
||
const up = dy < -26 || (dy < -12 && vy < -0.2);
|
||
const down = dy > 36 || (dy > 18 && vy > 0.28);
|
||
if(sheetMode==='expanded' && down){ openPeek(); return; }
|
||
if(sheetMode==='peek' && up){ openExpanded(); return; }
|
||
if(sheetMode==='peek' && down){ closeSheet(); return; }
|
||
}
|
||
sheet.style.transform='translateY(0)';
|
||
setTimeout(()=>{ sheet.style.transition=''; }, 300);
|
||
const ht=document.getElementById('dragText'); if(ht) ht.textContent='pull up • swipe down to close';
|
||
const gb=document.getElementById('grabBar'); if(gb){ gb.style.width=''; gb.style.background=''; }
|
||
}
|
||
// big drag area – handle + top 96px
|
||
handleEl.addEventListener('pointerdown', e=>{ pd(e.clientY, e); try{handleEl.setPointerCapture(e.pointerId)}catch{} });
|
||
sheet.addEventListener('pointerdown', e=>{
|
||
const r=sheet.getBoundingClientRect();
|
||
const y=e.clientY - r.top;
|
||
const interactive=e.target.closest('button,input,a,select,textarea,label');
|
||
if(y < 96 || !interactive) pd(e.clientY, e);
|
||
});
|
||
window.addEventListener('pointermove', e=> moveDrag(e), {passive:false});
|
||
function moveDrag(e){ const y=e.clientY ?? (e.touches && e.touches[0].clientY); if(y!=null) pm(y, e) }
|
||
window.addEventListener('pointerup', pu);
|
||
window.addEventListener('pointercancel', pu);
|
||
// touch
|
||
handleEl.addEventListener('touchstart', e=> pd(e.touches[0].clientY, e), {passive:false});
|
||
sheet.addEventListener('touchstart', e=>{
|
||
const rect=sheet.getBoundingClientRect();
|
||
const y=e.touches[0].clientY-rect.top;
|
||
const t=e.target;
|
||
if(y < 100 || !t.closest('button,input,label,a,select,textarea')) pd(e.touches[0].clientY, e);
|
||
}, {passive:false});
|
||
window.addEventListener('touchmove', e=>{ if(dragging){ pm(e.touches[0].clientY, e); e.preventDefault() }}, {passive:false});
|
||
window.addEventListener('touchend', pu);
|
||
window.addEventListener('touchcancel', pu);
|
||
|
||
// start closed
|
||
closeSheet();
|
||
</script>
|
||
</body>
|
||
</html> |