Files
yovision/docs/raw/提案-寄宿制学校.html
T

784 lines
26 KiB
HTML

<title>校园夜间安全预警系统 · 提案</title>
<style>
:root{
/* 纸面:带极淡青灰偏色的账簿纸,非纯白非奶油 */
--paper: #EBEDE8;
--paper-raised: #F7F8F5;
--ink: #1B2A33;
--ink-soft: #566A76;
--rule: #C6CEC8;
/* 夜间:值班室 2 点钟的监视墙,固定不随主题反转 */
--night: #0F1A20;
--night-raised: #16232B;
--night-ink: #E4E9E5;
--night-soft: #8FA3AD;
--night-rule: #26343D;
/* 信号:校园钠灯 / 值班室警示灯 */
--sodium: #E9A13C;
/* 已处置:老黑板绿 */
--board: #3A7A72;
--font-body: "PingFang SC","Hiragino Sans GB","Microsoft YaHei","Source Han Sans SC","Noto Sans CJK SC","Heiti SC",sans-serif;
--font-display: "Songti SC","STSong","Source Han Serif SC","Noto Serif CJK SC","SimSun",serif;
--font-mono: ui-monospace,"SF Mono","Menlo","Consolas","Liberation Mono",monospace;
--pad: clamp(28px, 5vw, 88px);
--maxw: 1080px;
}
@media (prefers-color-scheme: dark){
:root{
--paper: #131B20;
--paper-raised: #1A242A;
--ink: #E3E8E4;
--ink-soft: #96A7B0;
--rule: #2B383F;
}
}
:root[data-theme="dark"]{
--paper: #131B20;
--paper-raised: #1A242A;
--ink: #E3E8E4;
--ink-soft: #96A7B0;
--rule: #2B383F;
}
:root[data-theme="light"]{
--paper: #EBEDE8;
--paper-raised: #F7F8F5;
--ink: #1B2A33;
--ink-soft: #566A76;
--rule: #C6CEC8;
}
*{ box-sizing:border-box; }
.deck{
font-family: var(--font-body);
height:100dvh;
overflow-y:auto;
scroll-snap-type: y mandatory;
scroll-behavior: smooth;
background: var(--paper);
}
.slide{
--slide-bg: var(--paper);
--slide-ink: var(--ink);
--slide-soft: var(--ink-soft);
--slide-rule: var(--rule);
scroll-snap-align: start;
min-height:100dvh;
padding: var(--pad);
display:flex;
flex-direction:column;
justify-content:center;
background: var(--slide-bg);
color: var(--slide-ink);
position:relative;
}
.slide.night{
--slide-bg: var(--night);
--slide-ink: var(--night-ink);
--slide-soft: var(--night-soft);
--slide-rule: var(--night-rule);
}
.wrap{ width:100%; max-width:var(--maxw); margin:0 auto; }
/* 眉标:借值班记录的抬头写法 */
.eyebrow{
font-family: var(--font-mono);
font-size:12px;
letter-spacing:.16em;
color: var(--slide-soft);
display:flex;
gap:14px;
align-items:center;
margin-bottom: clamp(20px,3.4vh,40px);
}
.eyebrow::after{
content:"";
flex:1;
height:1px;
background: var(--slide-rule);
}
h1{
font-family: var(--font-display);
font-weight:700;
font-size: clamp(34px, 6.2vw, 78px);
line-height:1.16;
letter-spacing:.01em;
text-wrap: balance;
margin:0;
}
h2{
font-family: var(--font-display);
font-weight:700;
font-size: clamp(28px, 4.4vw, 52px);
line-height:1.22;
text-wrap: balance;
margin:0 0 .5em;
}
.lede{
font-size: clamp(16px, 1.9vw, 21px);
line-height:1.75;
color: var(--slide-soft);
max-width: 34em;
margin:0;
}
.kicker{
font-size: clamp(17px, 2.1vw, 24px);
line-height:1.7;
max-width: 30em;
margin: clamp(24px,3.5vh,44px) 0 0;
padding-left: 20px;
border-left: 3px solid var(--sodium);
}
/* ── 封面 ── */
.s-cover h1{ font-size: clamp(38px, 7.4vw, 92px); }
.cover-sub{
font-size: clamp(15px,1.8vw,19px);
color: var(--slide-soft);
margin: clamp(20px,3vh,32px) 0 0;
letter-spacing:.02em;
}
.window{
margin-top: clamp(40px,7vh,80px);
display:flex;
align-items:center;
gap:16px;
font-family: var(--font-mono);
font-size:13px;
letter-spacing:.1em;
color: var(--slide-soft);
}
.window .bar{
flex:1;
height:1px;
background: linear-gradient(90deg, var(--slide-rule), var(--sodium), var(--slide-rule));
}
/* ── 数据条 ── */
.stats{
display:grid;
grid-template-columns: repeat(auto-fit, minmax(180px,1fr));
gap: clamp(20px,3vw,48px);
margin: clamp(28px,4vh,48px) 0 0;
border-top:1px solid var(--slide-rule);
padding-top: clamp(22px,3vh,34px);
}
.stat .n{
font-family: var(--font-mono);
font-variant-numeric: tabular-nums;
font-size: clamp(32px,4.6vw,54px);
line-height:1;
letter-spacing:-.02em;
}
.stat .n.hi{ color: var(--sodium); }
.stat .l{
margin-top:10px;
font-size:14px;
line-height:1.6;
color: var(--slide-soft);
}
/* ── 值班日志 / 时间线 ── */
.log{
list-style:none;
margin: clamp(18px,3vh,30px) 0 0;
padding:0;
border-left:1px solid var(--slide-rule);
}
.log li{
display:grid;
grid-template-columns: 82px 1fr auto;
gap: clamp(12px,2vw,26px);
align-items:baseline;
padding: clamp(11px,1.7vh,17px) 0 clamp(11px,1.7vh,17px) clamp(16px,2.4vw,30px);
border-bottom:1px solid var(--slide-rule);
position:relative;
}
.log li::before{
content:"";
position:absolute;
left:-3px; top:50%;
width:5px; height:5px;
border-radius:50%;
background: var(--slide-rule);
transform: translateY(-50%);
}
.log li.hit::before{ background: var(--sodium); box-shadow:0 0 0 4px rgba(233,161,60,.16); }
.log time{
font-family: var(--font-mono);
font-variant-numeric: tabular-nums;
font-size: clamp(13px,1.5vw,16px);
color: var(--slide-soft);
letter-spacing:.02em;
}
.log li.hit time{ color: var(--sodium); }
.log p{
margin:0;
font-size: clamp(14px,1.7vw,18px);
line-height:1.6;
}
.tag{
font-size:12px;
letter-spacing:.06em;
color: var(--slide-soft);
white-space:nowrap;
}
.tag.blind{ opacity:.55; }
.tag.act{ color: var(--sodium); }
.tag.done{ color: var(--board); }
.total{
margin-top: clamp(22px,3.4vh,36px);
display:flex;
flex-wrap:wrap;
align-items:baseline;
gap: 6px 18px;
}
.total .lab{ font-size:15px; color: var(--slide-soft); }
.total .val{
font-family: var(--font-mono);
font-variant-numeric: tabular-nums;
font-size: clamp(28px,4.2vw,50px);
line-height:1;
color: var(--sodium);
letter-spacing:-.02em;
}
.total .val.good{ color: var(--board); }
/* ── 规则卡 ── */
.rules{
display:grid;
grid-template-columns: repeat(auto-fit, minmax(230px,1fr));
gap:1px;
margin-top: clamp(24px,3.6vh,40px);
background: var(--slide-rule);
border:1px solid var(--slide-rule);
}
.rule{
background: var(--slide-bg);
padding: clamp(20px,2.6vw,30px);
display:flex;
flex-direction:column;
gap:10px;
}
.rule .when{
font-family: var(--font-mono);
font-size:12px;
letter-spacing:.08em;
color: var(--sodium);
}
.rule h3{
margin:0;
font-size: clamp(17px,2vw,21px);
font-weight:600;
letter-spacing:.01em;
}
.rule p{
margin:0;
font-size:14px;
line-height:1.65;
color: var(--slide-soft);
}
/* ── 链路 ── */
.chain{
display:flex;
flex-wrap:wrap;
align-items:stretch;
gap:10px;
margin: clamp(24px,3.6vh,40px) 0 0;
}
.step{
flex:1 1 150px;
border:1px solid var(--slide-rule);
padding: clamp(15px,2vw,20px);
display:flex;
flex-direction:column;
gap:8px;
}
.step.key{ border-color: var(--sodium); }
.step .t{
font-family: var(--font-mono);
font-size:11px;
letter-spacing:.1em;
color: var(--slide-soft);
}
.step.key .t{ color: var(--sodium); }
.step .d{ font-size: clamp(14px,1.6vw,17px); line-height:1.5; }
/* ── 要点列表 ── */
.points{
list-style:none;
margin: clamp(22px,3.4vh,38px) 0 0;
padding:0;
display:flex;
flex-direction:column;
gap: clamp(13px,1.9vh,19px);
max-width:44em;
}
.points li{
display:grid;
grid-template-columns: 18px 1fr;
gap:16px;
font-size: clamp(15px,1.8vw,19px);
line-height:1.65;
}
.points li::before{
content:"";
width:8px; height:1px;
background: var(--sodium);
margin-top:.85em;
}
.points .sub{
display:block;
font-size:14px;
color: var(--slide-soft);
margin-top:5px;
line-height:1.6;
}
/* ── 投入结构 ── */
.cost{
display:grid;
grid-template-columns: repeat(auto-fit, minmax(280px,1fr));
gap: clamp(18px,2.6vw,30px);
margin: clamp(24px,3.6vh,40px) 0 0;
}
.cost .box{
border-top:2px solid var(--slide-ink);
padding-top:18px;
}
.cost .box.b2{ border-top-color: var(--sodium); }
.cost h3{
margin:0 0 4px;
font-size: clamp(17px,2vw,21px);
font-weight:600;
}
.cost .note{ font-size:13px; color: var(--slide-soft); margin:0 0 14px; }
.cost ul{ margin:0; padding-left:1.1em; display:flex; flex-direction:column; gap:8px; }
.cost li{ font-size:15px; line-height:1.6; color: var(--slide-soft); }
.foot{
margin-top: clamp(26px,4vh,44px);
padding-top:18px;
border-top:1px solid var(--slide-rule);
font-size:14px;
line-height:1.7;
color: var(--slide-soft);
max-width:46em;
}
/* ── 下一步 ── */
.steps{
list-style:none;
counter-reset: st;
margin: clamp(24px,3.6vh,42px) 0 0;
padding:0;
display:grid;
grid-template-columns: repeat(auto-fit, minmax(230px,1fr));
gap: clamp(18px,2.6vw,28px);
}
.steps li{
counter-increment: st;
border-top:1px solid var(--slide-rule);
padding-top:16px;
display:flex;
flex-direction:column;
gap:8px;
}
.steps li::before{
content: counter(st,decimal-leading-zero);
font-family: var(--font-mono);
font-size:12px;
letter-spacing:.1em;
color: var(--sodium);
}
.steps h3{ margin:0; font-size: clamp(16px,1.9vw,20px); font-weight:600; }
.steps p{ margin:0; font-size:14px; line-height:1.6; color: var(--slide-soft); }
/* ── HUD ── */
.hud{
position:fixed;
right: clamp(14px,2.2vw,26px);
bottom: clamp(14px,2.2vw,24px);
z-index:20;
font-family: var(--font-mono);
font-variant-numeric: tabular-nums;
font-size:12px;
letter-spacing:.1em;
color: var(--ink-soft);
background: color-mix(in srgb, var(--paper) 84%, transparent);
border:1px solid var(--rule);
padding:6px 11px;
backdrop-filter: blur(6px);
pointer-events:none;
}
.progress{
position:fixed;
top:0; left:0;
height:2px;
background: var(--sodium);
z-index:21;
width:0;
transition: width .28s ease;
}
.hint{
position:fixed;
left: clamp(14px,2.2vw,26px);
bottom: clamp(14px,2.2vw,24px);
z-index:20;
font-family: var(--font-mono);
font-size:11px;
letter-spacing:.08em;
color: var(--ink-soft);
opacity:.6;
pointer-events:none;
transition: opacity .4s ease;
}
.hint.gone{ opacity:0; }
/* ── 入场 ── */
.deck.js .slide .wrap > *{
opacity:0;
transform: translateY(14px);
transition: opacity .55s ease, transform .55s ease;
}
.deck.js .slide.in .wrap > *{ opacity:1; transform:none; }
.deck.js .slide.in .wrap > *:nth-child(2){ transition-delay:.07s; }
.deck.js .slide.in .wrap > *:nth-child(3){ transition-delay:.14s; }
.deck.js .slide.in .wrap > *:nth-child(4){ transition-delay:.21s; }
.deck.js .slide.in .wrap > *:nth-child(5){ transition-delay:.28s; }
@media (prefers-reduced-motion: reduce){
.deck{ scroll-behavior:auto; }
.deck.js .slide .wrap > *{ opacity:1; transform:none; transition:none; }
.progress{ transition:none; }
}
:focus-visible{ outline:2px solid var(--sodium); outline-offset:3px; }
@media (max-width:640px){
.log li{ grid-template-columns: 68px 1fr; }
.log .tag{ grid-column: 2; }
}
</style>
<div class="progress" id="prog"></div>
<div class="deck" id="deck">
<!-- 1 封面 -->
<section class="slide s-cover">
<div class="wrap">
<p class="eyebrow">YOVISION · 校园安全预警系统</p>
<h1>夜里出事,<br>第一个知道的应该是学校</h1>
<p class="cover-sub">面向寄宿制学校的视频行为预警方案 · 方案提案</p>
<div class="window">
<span>22:30</span><span class="bar"></span><span>06:00</span>
</div>
<p class="cover-sub">那道没有人在看的窗口</p>
</div>
</section>
<!-- 2 现状 -->
<section class="slide">
<div class="wrap">
<p class="eyebrow">现状</p>
<h2>摄像头装了,晚上谁在看?</h2>
<p class="lede">这不是设备问题。绝大多数寄宿制学校的监控覆盖已经够用了,缺的是夜里那几个小时里,有人替你盯着这些画面。</p>
<div class="stats">
<div class="stat"><div class="n">240</div><div class="l">在用摄像头(1200 人规模典型配置)</div></div>
<div class="stat"><div class="n">2</div><div class="l">夜间在岗人数 · 宿管 1 门卫 1</div></div>
<div class="stat"><div class="n">16</div><div class="l">值班屏能同时显示的画面数</div></div>
<div class="stat"><div class="n hi">0</div><div class="l">凌晨真正被人看过的画面</div></div>
</div>
<p class="kicker">监控让你事后查得到,但它不会在半夜叫醒任何人。</p>
</div>
</section>
<!-- 3 夜晚 A -->
<section class="slide night">
<div class="wrap">
<p class="eyebrow">情景推演 · 现在的夜晚</p>
<h2>一个普通的周三夜里</h2>
<ol class="log">
<li><time>22:30</time><p>熄灯查寝完毕,两栋宿舍楼安静</p><span class="tag">正常</span></li>
<li><time>23:10</time><p>两名学生从操场东侧围墙翻出</p><span class="tag blind">无人知晓</span></li>
<li><time>01:40</time><p>值班屏画面正常轮巡,无人在岗查看</p><span class="tag blind">无人知晓</span></li>
<li><time>05:50</time><p>早操点名,两人不在队列</p><span class="tag">发现异常</span></li>
<li><time>06:15</time><p>学校组织人手开始寻找,调取录像回溯</p><span class="tag">被动响应</span></li>
</ol>
<div class="total">
<span class="lab">事情发生 → 学校知情</span>
<span class="val">7 小时 45 分</span>
</div>
</div>
</section>
<!-- 4 夜晚 B -->
<section class="slide night">
<div class="wrap">
<p class="eyebrow">情景推演 · 同一个夜晚,装了系统</p>
<h2>同样是这堵墙</h2>
<ol class="log">
<li><time>22:30</time><p>熄灯查寝完毕,系统进入夜间规则</p><span class="tag">布防</span></li>
<li class="hit"><time>23:10:04</time><p>围墙东侧检测到翻越动作</p><span class="tag act">命中规则</span></li>
<li class="hit"><time>23:10:06</time><p>值班室大屏弹窗 + 声光,附现场画面</p><span class="tag act">已送达</span></li>
<li class="hit"><time>23:10:06</time><p>宿管、保卫科手机同时推送</p><span class="tag act">已送达</span></li>
<li><time>23:10:31</time><p>值班员点击确认,前往东侧围墙</p><span class="tag done">已确认</span></li>
<li><time>23:14</time><p>学生在校外一百米处被截回</p><span class="tag done">已处置</span></li>
</ol>
<div class="total">
<span class="lab">事情发生 → 有人到场</span>
<span class="val good">4 分钟</span>
</div>
</div>
</section>
<!-- 5 我们做什么 -->
<section class="slide">
<div class="wrap">
<p class="eyebrow">我们做什么</p>
<h2>把已经装好的摄像头,<br>变成一个只在出事时叫人的系统</h2>
<p class="lede">不增加盯屏岗位,不改变现有值班安排。平时它一句话不说;一旦命中规则,它会一直找人,直到有人确认。</p>
<p class="kicker">不加人 · 不换设备 · 不增加值班负担</p>
</div>
</section>
<!-- 6 规则 -->
<section class="slide">
<div class="wrap">
<p class="eyebrow">先开这四条</p>
<h2>夜间最该被叫醒的四件事</h2>
<div class="rules">
<div class="rule">
<span class="when">全天 · 夜间加权</span>
<h3>翻越围墙护栏</h3>
<p>识别攀爬姿态与越线方向,区分「翻出去」和「路过」,避免把走读生放学误报成翻墙。</p>
</div>
<div class="rule">
<span class="when">22:30 — 06:00</span>
<h3>晚归与夜间离寝</h3>
<p>宿舍楼出入口机位,就寝后进出即记录,可按年级与楼栋分别设定时段。</p>
</div>
<div class="rule">
<span class="when">全天</span>
<h3>打架推搡与围观聚集</h3>
<p>识别剧烈肢体动作与人群聚拢,课间与就寝前是高发时段,秒级推到值班室。</p>
</div>
<div class="rule">
<span class="when">非开放时段</span>
<h3>危险区域闯入</h3>
<p>天台、配电房、水池、实验室,在图上圈出范围与生效时段,进入并停留即告警。</p>
</div>
</div>
<p class="lede" style="margin-top:22px">操场跌倒、上课时段楼道徘徊、外来人员尾随进校,可在运行稳定后逐条加开。</p>
</div>
</section>
<!-- 7 响应链路 -->
<section class="slide">
<div class="wrap">
<p class="eyebrow">响应链路</p>
<h2>报出去不算数,有人确认才算</h2>
<div class="chain">
<div class="step"><span class="t">01 检测</span><span class="d">命中规则</span></div>
<div class="step"><span class="t">02 取证</span><span class="d">自动回捞事发前十秒画面</span></div>
<div class="step"><span class="t">03 送达</span><span class="d">值班室大屏 + 声光 + 手机</span></div>
<div class="step key"><span class="t">04 确认</span><span class="d">值班员点击确认并处置</span></div>
<div class="step key"><span class="t">05 升级</span><span class="d">超时无人确认,自动往上找人</span></div>
</div>
<p class="kicker">发出去和被人看到,是两回事。没有人确认,系统会自己升级到保卫科长、再到值班校领导——不会停在「已发送」。</p>
</div>
</section>
<!-- 8 不换设备 -->
<section class="slide">
<div class="wrap">
<p class="eyebrow">怎么落地</p>
<h2>不重新布线,不换品牌</h2>
<ul class="points">
<li><span>接入贵校现有的网络摄像头,支持标准协议即可,不限品牌型号。<span class="sub">开工前先做半天勘测,出一份点位报告:哪些机位现在就能用、哪些需要补光或调角度。不承诺坏机位的效果。</span></span></li>
<li><span>校内放一台推理服务器,视频与分析全部在校园网内完成。<span class="sub">视频不出校。上传到平台的只有告警事件本身和那一小段证据片段。</span></span></li>
<li><span>值班室加一块大屏和一套声光,其余沿用现有设备。</span></li>
<li><span>规则、区域、时段在网页上配置,学期变化自己就能改。</span></li>
</ul>
</div>
</section>
<!-- 9 不做人脸 -->
<section class="slide night">
<div class="wrap">
<p class="eyebrow">关于隐私</p>
<h2>我们不做人脸识别</h2>
<ul class="points">
<li><span>不建立学生人脸库,不采集学生面部特征。</span></li>
<li><span>系统判断的是「有人翻墙」,不是「谁翻了墙」。<span class="sub">告警推给值班员的是位置、时间和画面,由老师到现场自己看是哪位同学。</span></span></li>
<li><span>跨摄像头跟踪使用匿名标识,仅当次有效,不留档、不关联到具体学生。</span></li>
<li><span>未成年人影像的合规要求是硬线,也是家长最先问的问题。<span class="sub">这一页的内容,可以直接写进给家长的说明里。</span></span></li>
</ul>
</div>
</section>
<!-- 10 试运行 -->
<section class="slide">
<div class="wrap">
<p class="eyebrow">怎么开始</p>
<h2>先看两周数据,<br>再决定要不要开告警</h2>
<p class="lede">第一周就用误报轰炸值班室,这套系统在贵校就再也没人信了。所以试运行期只统计、不告警——不打扰任何人的现有值班。</p>
<ul class="points">
<li><span>两周后交一份周报:本周检测到翻越多少次、集中在哪个时段和哪段围墙。</span></li>
<li><span>晚归多少人次,集中在哪栋楼哪个门。</span></li>
<li><span>误报多少次,分别是什么原因,已经怎么调整。</span></li>
<li><span>你看完这份数据,再决定正式开哪几条规则。</span></li>
</ul>
</div>
</section>
<!-- 11 平时价值 -->
<section class="slide">
<div class="wrap">
<p class="eyebrow">不止夜里有用</p>
<h2>平安校园检查,报表直接导出</h2>
<ul class="points">
<li><span>点位覆盖情况、告警处置率、平均响应时长,按月按学期一键导出。<span class="sub">迎检材料不用再临时拼,系统里本来就在记。</span></span></li>
<li><span>寒暑假切换成周界模式,全年不闲置。</span></li>
<li><span>每月一次安全分析:高发时段、高发点位、机位调整建议。<span class="sub">哪段围墙被翻得最多,数据会告诉你——这比加派人手更省。</span></span></li>
<li><span>每一条告警的完整记录留存可查:什么时候报的、报给了谁、谁在几点确认、怎么处置的。</span></li>
</ul>
</div>
</section>
<!-- 12 投入 -->
<section class="slide">
<div class="wrap">
<p class="eyebrow">投入结构</p>
<h2>两笔钱,分开算</h2>
<div class="cost">
<div class="box">
<h3>建设费</h3>
<p class="note">一次性</p>
<ul>
<li>校内推理服务器</li>
<li>平台部署与调试</li>
<li>存量摄像头接入</li>
<li>区域、时段与课表配置</li>
<li>值班室大屏与声光</li>
</ul>
</div>
<div class="box b2">
<h3>年服务费</h3>
<p class="note">按路数计,逐年</p>
<ul>
<li>规则随学期与课表调整</li>
<li>误报持续收敛</li>
<li>模型更新</li>
<li>迎检报表与月度分析</li>
<li>远程运维与故障响应</li>
</ul>
</div>
</div>
<p class="foot">具体报价需现场勘测后出具。哪些点位现在就能用、哪些需要补光或换机位,勘测报告里会逐条写清楚——我们不对达不到条件的机位承诺效果,也不把这部分算进合同。</p>
</div>
</section>
<!-- 13 下一步 -->
<section class="slide">
<div class="wrap">
<p class="eyebrow">下一步</p>
<h2>三步,不影响现有值班</h2>
<ol class="steps">
<li>
<h3>现场勘测</h3>
<p>半天时间,走一遍围墙、宿舍楼出入口和危险区域,出具点位可行性报告。</p>
</li>
<li>
<h3>试运行两周</h3>
<p>只统计不告警,值班室照常运转,不增加任何人的工作。</p>
</li>
<li>
<h3>看数据决定</h3>
<p>拿到周报后,由学校决定正式开哪几条规则、覆盖哪些点位。</p>
</li>
</ol>
<p class="foot">YoVision 校园安全预警系统 · 本方案中的夜间时间线为情景推演,用于说明响应流程,非真实事件记录。</p>
</div>
</section>
</div>
<div class="hud"><span id="cur">01</span> / <span id="tot">13</span></div>
<div class="hint" id="hint">↑ ↓ 翻页</div>
<script>
(function(){
var deck = document.getElementById('deck');
var slides = Array.prototype.slice.call(deck.querySelectorAll('.slide'));
var cur = document.getElementById('cur');
var tot = document.getElementById('tot');
var prog = document.getElementById('prog');
var hint = document.getElementById('hint');
var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var index = 0;
deck.classList.add('js');
tot.textContent = String(slides.length).padStart(2,'0');
function pad(n){ return String(n).padStart(2,'0'); }
function setIndex(i){
index = i;
cur.textContent = pad(i+1);
prog.style.width = ((i+1)/slides.length*100) + '%';
}
if ('IntersectionObserver' in window){
var io = new IntersectionObserver(function(entries){
entries.forEach(function(e){
if (e.isIntersecting){
e.target.classList.add('in');
setIndex(slides.indexOf(e.target));
}
});
}, { threshold: 0.55 });
slides.forEach(function(s){ io.observe(s); });
} else {
slides.forEach(function(s){ s.classList.add('in'); });
}
function go(i){
if (i < 0 || i >= slides.length) return;
slides[i].scrollIntoView({ behavior: reduce ? 'auto' : 'smooth', block:'start' });
if (hint) hint.classList.add('gone');
}
document.addEventListener('keydown', function(ev){
var k = ev.key;
if (k === 'ArrowDown' || k === 'ArrowRight' || k === 'PageDown' || k === ' '){
ev.preventDefault(); go(index + 1);
} else if (k === 'ArrowUp' || k === 'ArrowLeft' || k === 'PageUp'){
ev.preventDefault(); go(index - 1);
} else if (k === 'Home'){
ev.preventDefault(); go(0);
} else if (k === 'End'){
ev.preventDefault(); go(slides.length - 1);
}
});
deck.addEventListener('scroll', function(){
if (hint && deck.scrollTop > 40) hint.classList.add('gone');
}, { passive:true });
setIndex(0);
})();
</script>