2026-10-15 00:28:35 +00:00
<!doctype html>
< html lang = "en-AU" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
2026-10-15 00:31:30 +00:00
< meta http-equiv = "Content-Security-Policy" content = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' data:; connect-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'" >
2026-09-08 15:23:31 +10:00
< title > CycleWise AU</ title >
2026-10-15 00:31:30 +00:00
< link rel = "stylesheet" href = "fonts/fonts.css" >
2026-09-08 14:28:14 +10:00
< style >
: root {
--paper : #FBF7F2 ;
--surface : #FFFFFF ;
--surface-sunken : #F1E9E1 ;
--ink : #251C1F ;
--ink-soft : #6B5D5F ;
--line : rgba ( 37 , 28 , 31 , 0.14 );
--line-strong : rgba ( 37 , 28 , 31 , 0.28 );
--period : #A93349 ;
--period-strong : #7E2438 ;
--period-soft : #F3DCE0 ;
--ovulation : #B87A1F ;
--ovulation-strong : #8F5E14 ;
--ovulation-soft : #F4E3C4 ;
--fertile : #3B7A6C ;
--fertile-strong : #265A4F ;
--fertile-soft : #DCEBE5 ;
--pregnancy : #5B4B8A ;
--pregnancy-strong : #453768 ;
--pregnancy-soft : #E5E0F2 ;
--shadow : 0 1 px 2 px rgba ( 37 , 28 , 31 , 0.06 ), 0 8 px 24 px -12 px rgba ( 37 , 28 , 31 , 0.18 );
}
@ media ( prefers-color-scheme : dark ) {
: root : not ([ data-theme = "light" ]) {
--paper : #1B1417 ;
--surface : #241B1F ;
--surface-sunken : #2C2226 ;
--ink : #F3E9E6 ;
--ink-soft : #B8A6AA ;
--line : rgba ( 243 , 233 , 230 , 0.14 );
--line-strong : rgba ( 243 , 233 , 230 , 0.26 );
--period : #E58A96 ;
--period-strong : #F0AEB6 ;
--period-soft : #402129 ;
--ovulation : #E3AC5C ;
--ovulation-strong : #EFC585 ;
--ovulation-soft : #3E2F19 ;
--fertile : #7FC4B2 ;
--fertile-strong : #A3D8C9 ;
--fertile-soft : #1E332D ;
--pregnancy : #B0A0E0 ;
--pregnancy-strong : #CBC0EC ;
--pregnancy-soft : #2C2540 ;
--shadow : 0 1 px 2 px rgba ( 0 , 0 , 0 , 0.3 ), 0 12 px 28 px -14 px rgba ( 0 , 0 , 0 , 0.6 );
}
}
: root [ data-theme = "dark" ] {
--paper : #1B1417 ;
--surface : #241B1F ;
--surface-sunken : #2C2226 ;
--ink : #F3E9E6 ;
--ink-soft : #B8A6AA ;
--line : rgba ( 243 , 233 , 230 , 0.14 );
--line-strong : rgba ( 243 , 233 , 230 , 0.26 );
--period : #E58A96 ;
--period-strong : #F0AEB6 ;
--period-soft : #402129 ;
--ovulation : #E3AC5C ;
--ovulation-strong : #EFC585 ;
--ovulation-soft : #3E2F19 ;
--fertile : #7FC4B2 ;
--fertile-strong : #A3D8C9 ;
--fertile-soft : #1E332D ;
--pregnancy : #B0A0E0 ;
--pregnancy-strong : #CBC0EC ;
--pregnancy-soft : #2C2540 ;
--shadow : 0 1 px 2 px rgba ( 0 , 0 , 0 , 0.3 ), 0 12 px 28 px -14 px rgba ( 0 , 0 , 0 , 0.6 );
}
* { box-sizing : border-box ;}
body {
margin : 0 ;
background : var ( -- paper );
color : var ( -- ink );
font-family : "Work Sans" , system-ui , sans-serif ;
-webkit- font-smoothing : antialiased ;
}
h1 , h2 , h3 , . display {
font-family : "Fraunces" , Georgia , serif ;
text-wrap : balance ;
margin : 0 ;
}
. mono { font-family : "IBM Plex Mono" , ui-monospace , monospace ;}
a { color : inherit ;}
button { font-family : inherit ;}
:: selection { background : var ( -- period - soft );}
. app { max-width : 1180 px ; margin : 0 auto ; padding : 0 clamp ( 16 px , 4 vw , 40 px ) 96 px ;}
/* ---------- topbar ---------- */
. topbar {
display : flex ; align-items : center ; justify-content : space-between ;
gap : 20 px ; padding : 28 px 0 20 px ; flex-wrap : wrap ;
}
. wordmark { display : flex ; align-items : baseline ; gap : 10 px ;}
. wordmark . glyph {
width : 30 px ; height : 30 px ; flex : none ;
}
. wordmark . name { font-size : 1.35 rem ; font-weight : 600 ; letter-spacing : -0.01 em ;}
. wordmark . tag {
font-family : "IBM Plex Mono" , monospace ; font-size : 0.68 rem ; color : var ( -- ink - soft );
text-transform : uppercase ; letter-spacing : 0.09 em ;
}
. mode-switch {
display : flex ; gap : 4 px ; background : var ( -- surface - sunken ); padding : 4 px ; border-radius : 999 px ;
border : 1 px solid var ( -- line );
}
. mode-btn {
border : none ; background : transparent ; color : var ( -- ink - soft );
padding : 9 px 18 px ; border-radius : 999 px ; font-size : 0.86 rem ; font-weight : 600 ;
cursor : pointer ; transition : background .18 s ease , color .18 s ease ;
font-family : "Work Sans" , sans-serif ;
}
. mode-btn : hover { color : var ( -- ink );}
. mode-btn : focus-visible { outline : 2 px solid var ( -- ink ); outline-offset : 2 px ;}
. mode-btn [ data-mode = "period" ] . active { background : var ( -- period ); color : #fff ;}
. mode-btn [ data-mode = "ovulation" ] . active { background : var ( -- ovulation ); color : #fff ;}
. mode-btn [ data-mode = "pregnancy" ] . active { background : var ( -- pregnancy ); color : #fff ;}
/* ---------- panels ---------- */
. mode-panel { display : none ;}
. mode-panel . active { display : block ; animation : rise .4 s ease both ;}
@ keyframes rise { from { opacity : 0 ; transform : translateY ( 6 px );} to { opacity : 1 ; transform : none ;}}
@ media ( prefers-reduced-motion : reduce ) {. mode-panel . active { animation : none ;}}
2026-10-15 00:28:35 +00:00
@ media ( prefers-color-scheme : dark ) {. mode-btn [ data-mode = "pregnancy" ] . active { color : #1B1417 ;}}
2026-09-08 14:28:14 +10:00
. hero {
display : grid ; grid-template-columns : 1.3 fr 1 fr ; gap : 40 px ;
padding : 36 px 0 28 px ; border-bottom : 1 px solid var ( -- line );
align-items : center ;
}
@ media ( max-width : 820px ) {. hero { grid-template-columns : 1 fr ;}}
. eyebrow {
font-family : "IBM Plex Mono" , monospace ; font-size : 0.72 rem ; letter-spacing : 0.12 em ;
text-transform : uppercase ; font-weight : 500 ; margin-bottom : 10 px ; display : inline-block ;
}
. hero h1 { font-size : clamp ( 1.9 rem , 3.4 vw , 2.7 rem ); font-weight : 600 ; line-height : 1.08 ;}
. hero p . lede { color : var ( -- ink - soft ); font-size : 1.02 rem ; line-height : 1.6 ; max-width : 52 ch ; margin-top : 14 px ;}
2026-10-15 00:22:53 +00:00
. hero-art { display : flex ; justify-content : center ; position : relative ;}
. hero-art . has-3d { align-items : center ;}
. hero-art . has-3d . ring3d { position : absolute ; top : 50 % ; left : 50 % ; transform : translate ( -50 % , -50 % );}
. hero-art . has-3d svg { position : relative ; max-width : 300 px ;}
. hero-art . has-3d svg path , . hero-art . has-3d svg circle { display : none ;}
2026-09-08 14:28:14 +10:00
. hero-art svg { width : 100 % ; max-width : 220 px ; height : auto ;}
. stat-row { display : flex ; gap : 12 px ; flex-wrap : wrap ; margin-top : 22 px ;}
. stat-tile {
background : var ( -- surface ); border : 1 px solid var ( -- line ); border-radius : 14 px ;
padding : 12 px 16 px ; min-width : 118 px ; box-shadow : var ( -- shadow );
}
. stat-tile . num { font-family : "Fraunces" , serif ; font-size : 1.5 rem ; font-weight : 600 ; line-height : 1 ;}
. stat-tile . lbl { font-size : 0.72 rem ; color : var ( -- ink - soft ); margin-top : 4 px ; text-transform : uppercase ; letter-spacing : 0.05 em ;}
. controls { display : flex ; gap : 16 px ; flex-wrap : wrap ; margin-top : 22 px ;}
. field { display : flex ; flex-direction : column ; gap : 6 px ;}
. field label { font-size : 0.72 rem ; text-transform : uppercase ; letter-spacing : 0.06 em ; color : var ( -- ink - soft ); font-weight : 600 ;}
. field input {
font-family : "IBM Plex Mono" , monospace ; font-size : 0.88 rem ; padding : 9 px 11 px ;
border : 1 px solid var ( -- line - strong ); border-radius : 9 px ; background : var ( -- surface ); color : var ( -- ink );
width : 150 px ;
}
. field input : focus-visible { outline : 2 px solid var ( -- ink ); outline-offset : 1 px ;}
. example-note { font-size : 0.76 rem ; color : var ( -- ink - soft ); margin-top : 14 px ; font-style : italic ;}
/* ---------- calendar layout ---------- */
. workbench {
display : grid ; grid-template-columns : 1.5 fr 1 fr ; gap : 28 px ; padding : 32 px 0 ;
}
@ media ( max-width : 900px ) {. workbench { grid-template-columns : 1 fr ;}}
. cal-card {
background : var ( -- surface ); border : 1 px solid var ( -- line ); border-radius : 20 px ;
padding : 22 px 22 px 18 px ; box-shadow : var ( -- shadow );
}
. cal-head { display : flex ; align-items : center ; justify-content : space-between ; margin-bottom : 14 px ;}
. cal-head h3 { font-size : 1.15 rem ; font-weight : 600 ;}
. cal-nav { display : flex ; gap : 6 px ;}
. nav-btn {
width : 32 px ; height : 32 px ; border-radius : 9 px ; border : 1 px solid var ( -- line - strong );
background : var ( -- surface ); color : var ( -- ink ); cursor : pointer ; font-size : 1 rem ;
display : flex ; align-items : center ; justify-content : center ;
}
. nav-btn : hover { background : var ( -- surface - sunken );}
. weekday-row { display : grid ; grid-template-columns : repeat ( 7 , 1 fr ); gap : 6 px ; margin-bottom : 6 px ;}
. weekday-row span {
text-align : center ; font-size : 0.68 rem ; color : var ( -- ink - soft );
text-transform : uppercase ; letter-spacing : 0.05 em ; font-weight : 600 ;
}
. day-grid { display : grid ; grid-template-columns : repeat ( 7 , 1 fr ); gap : 6 px ;}
. day-cell {
aspect-ratio : 1 ; border-radius : 10 px ; border : 1 px solid transparent ;
background : var ( -- surface - sunken ); cursor : pointer ; position : relative ;
display : flex ; flex-direction : column ; align-items : center ; justify-content : center ;
font-family : "IBM Plex Mono" , monospace ; font-size : 0.78 rem ; color : var ( -- ink );
transition : transform .12 s ease ;
}
. day-cell : hover { transform : translateY ( -1 px ); border-color : var ( -- line - strong );}
. day-cell : focus-visible { outline : 2 px solid var ( -- ink ); outline-offset : 1 px ;}
. day-cell . pad { visibility : hidden ; pointer-events : none ;}
. day-cell . dot-row { display : flex ; gap : 2 px ; position : absolute ; bottom : 4 px ;}
. day-cell . dot { width : 4 px ; height : 4 px ; border-radius : 50 % ;}
. day-cell . today { box-shadow : inset 0 0 0 2 px var ( -- ink );}
. day-cell . selected { box-shadow : inset 0 0 0 2 px var ( -- ink ); background : var ( -- surface );}
. day-cell . is-period { background : var ( -- period - soft );}
. day-cell . is-fertile { background : var ( -- fertile - soft );}
. day-cell . is-ovulation { background : var ( -- ovulation - soft ); box-shadow : inset 0 0 0 2 px var ( -- ovulation - strong );}
. day-cell . is-fertile . is-ovulation { box-shadow : inset 0 0 0 2 px var ( -- ovulation - strong );}
. legend { display : flex ; gap : 14 px ; flex-wrap : wrap ; margin-top : 16 px ; font-size : 0.76 rem ; color : var ( -- ink - soft );}
. legend span { display : inline-flex ; align-items : center ; gap : 6 px ;}
. legend i { width : 10 px ; height : 10 px ; border-radius : 3 px ; display : inline-block ;}
. detail-card {
background : var ( -- surface ); border : 1 px solid var ( -- line ); border-radius : 20 px ;
padding : 24 px ; box-shadow : var ( -- shadow ); display : flex ; flex-direction : column ; gap : 14 px ;
}
. detail-card . d-date { font-family : "IBM Plex Mono" , monospace ; font-size : 0.76 rem ; color : var ( -- ink - soft ); text-transform : uppercase ; letter-spacing : 0.06 em ;}
. detail-card . d-phase { font-size : 1.4 rem ; font-weight : 600 ; font-family : "Fraunces" , serif ;}
. pill {
display : inline-flex ; align-self : flex-start ; padding : 4 px 12 px ; border-radius : 999 px ;
font-size : 0.72 rem ; font-weight : 600 ; text-transform : uppercase ; letter-spacing : 0.05 em ;
}
. d-desc { color : var ( -- ink - soft ); line-height : 1.6 ; font-size : 0.94 rem ;}
. d-grid { display : grid ; grid-template-columns : 1 fr 1 fr ; gap : 10 px ; margin-top : 4 px ;}
. d-mini { background : var ( -- surface - sunken ); border-radius : 12 px ; padding : 10 px 12 px ;}
. d-mini . k { font-size : 0.66 rem ; text-transform : uppercase ; letter-spacing : 0.06 em ; color : var ( -- ink - soft );}
. d-mini . v { font-size : 0.92 rem ; font-weight : 600 ; margin-top : 2 px ;}
/* ---------- reference cards ---------- */
. ref-heading { padding : 8 px 0 18 px ;}
. ref-heading h2 { font-size : 1.4 rem ; font-weight : 600 ;}
. ref-grid { display : grid ; grid-template-columns : repeat ( 4 , 1 fr ); gap : 14 px ;}
@ media ( max-width : 900px ) {. ref-grid { grid-template-columns : repeat ( 2 , 1 fr );}}
@ media ( max-width : 520px ) {. ref-grid { grid-template-columns : 1 fr ;}}
. ref-card {
background : var ( -- surface ); border : 1 px solid var ( -- line ); border-radius : 16 px ; padding : 18 px ;
display : flex ; flex-direction : column ; gap : 8 px ; box-shadow : var ( -- shadow );
}
. ref-card . r-num { font-family : "IBM Plex Mono" , monospace ; font-size : 0.7 rem ; color : var ( -- ink - soft );}
. ref-card h4 { font-size : 1.05 rem ; font-weight : 600 ; font-family : "Fraunces" , serif ;}
. ref-card p { font-size : 0.86 rem ; color : var ( -- ink - soft ); line-height : 1.55 ; margin : 0 ;}
. ref-card . tag-row { display : flex ; gap : 6 px ; flex-wrap : wrap ; margin-top : 2 px ;}
. tagchip { font-size : 0.68 rem ; background : var ( -- surface - sunken ); padding : 3 px 9 px ; border-radius : 999 px ; color : var ( -- ink - soft );}
/* ---------- ovulation timeline ---------- */
. timeline { display : flex ; gap : 6 px ; overflow-x : auto ; padding : 6 px 2 px 16 px ;}
. tl-btn {
flex : none ; min-width : 56 px ; border-radius : 12 px ; border : 1 px solid var ( -- line );
background : var ( -- surface ); padding : 10 px 4 px ; cursor : pointer ; text-align : center ;
}
. tl-btn . tl-day { font-family : "IBM Plex Mono" , monospace ; font-size : 0.72 rem ; color : var ( -- ink - soft );}
. tl-btn . tl-fert { width : 8 px ; height : 8 px ; border-radius : 50 % ; margin : 6 px auto 0 ;}
. tl-btn . active { box-shadow : inset 0 0 0 2 px var ( -- ovulation - strong ); background : var ( -- ovulation - soft );}
/* ---------- pregnancy week scrubber ---------- */
. trimester-track { position : relative ; margin : 26 px 0 8 px ;}
. trimester-labels { display : flex ; justify-content : space-between ; font-size : 0.72 rem ; color : var ( -- ink - soft );
text-transform : uppercase ; letter-spacing : 0.05 em ; margin-bottom : 8 px ; font-weight : 600 ;}
input [ type = "range" ] . week-slider {
width : 100 % ; -webkit- appearance : none ; appearance : none ; height : 8 px ; border-radius : 999 px ;
2026-10-15 00:24:38 +00:00
background : linear-gradient ( to right , var ( -- pregnancy ) 0 , var ( -- pregnancy ) calc ( 11 px + ( 100 % - 22 px ) * var ( -- f , 0 )), var ( -- pregnancy - soft ) calc ( 11 px + ( 100 % - 22 px ) * var ( -- f , 0 )), var ( -- pregnancy - soft ) 100 % );
2026-09-08 14:28:14 +10:00
outline : none ; cursor : pointer ;
}
input [ type = "range" ] . week-slider :: -webkit-slider-thumb {
-webkit- appearance : none ; width : 22 px ; height : 22 px ; border-radius : 50 % ;
background : var ( -- pregnancy ); border : 3 px solid var ( -- surface ); box-shadow : var ( -- shadow ); cursor : grab ;
}
input [ type = "range" ] . week-slider :: -moz-range-thumb {
width : 22 px ; height : 22 px ; border-radius : 50 % ; background : var ( -- pregnancy );
border : 3 px solid var ( -- surface ); box-shadow : var ( -- shadow ); cursor : grab ;
}
. week-ticks { display : flex ; justify-content : space-between ; margin-top : 6 px ;}
. week-ticks span { font-size : 0.64 rem ; color : var ( -- ink - soft ); font-family : "IBM Plex Mono" , monospace ;}
. week-panel {
display : grid ; grid-template-columns : 0.9 fr 1.4 fr ; gap : 28 px ; margin-top : 26 px ;
}
@ media ( max-width : 820px ) {. week-panel { grid-template-columns : 1 fr ;}}
. size-card {
background : var ( -- surface ); border : 1 px solid var ( -- line ); border-radius : 20 px ;
padding : 26 px ; box-shadow : var ( -- shadow ); text-align : center ;
display : flex ; flex-direction : column ; align-items : center ; gap : 10 px ; justify-content : center ;
}
. size-card . blob { display : flex ; align-items : center ; justify-content : center ; height : 120 px ;}
. size-card . cmp { font-family : "Fraunces" , serif ; font-size : 1.3 rem ; font-weight : 600 ;}
. size-card . metric { font-family : "IBM Plex Mono" , monospace ; font-size : 0.82 rem ; color : var ( -- ink - soft );}
. size-card . weeknum { font-size : 0.72 rem ; text-transform : uppercase ; letter-spacing : 0.08 em ; color : var ( -- pregnancy ); font-weight : 700 ;}
. week-info { display : flex ; flex-direction : column ; gap : 16 px ;}
. info-block { background : var ( -- surface ); border : 1 px solid var ( -- line ); border-radius : 16 px ; padding : 16 px 18 px ; box-shadow : var ( -- shadow );}
. info-block h5 { font-size : 0.72 rem ; text-transform : uppercase ; letter-spacing : 0.07 em ; color : var ( -- pregnancy - strong ); margin-bottom : 6 px ; font-weight : 700 ;}
. info-block p { margin : 0 ; font-size : 0.92 rem ; line-height : 1.55 ; color : var ( -- ink );}
. filmstrip { display : flex ; gap : 8 px ; overflow-x : auto ; padding : 4 px 2 px 20 px ; margin-top : 8 px ;}
. film-item {
2026-10-15 00:28:35 +00:00
color : var ( -- ink );
2026-09-08 14:28:14 +10:00
flex : none ; width : 64 px ; height : 64 px ; border-radius : 12 px ; border : 1 px solid var ( -- line );
background : var ( -- surface ); display : flex ; flex-direction : column ; align-items : center ; justify-content : center ;
cursor : pointer ; font-family : "IBM Plex Mono" , monospace ;
}
. film-item . fw { font-size : 0.62 rem ; color : var ( -- ink - soft );}
. film-item . fn { font-size : 1 rem ; font-weight : 700 ;}
. film-item . active { background : var ( -- pregnancy - soft ); box-shadow : inset 0 0 0 2 px var ( -- pregnancy );}
footer . note {
border-top : 1 px solid var ( -- line ); margin-top : 40 px ; padding-top : 20 px ;
font-size : 0.78 rem ; color : var ( -- ink - soft ); line-height : 1.6 ;
}
</ style >
2026-10-15 00:28:35 +00:00
</ head >
< body >
2026-09-08 14:28:14 +10:00
< div class = "app" >
< header class = "topbar" >
< div class = "wordmark" >
2026-09-08 15:23:31 +10:00
< svg class = "glyph" viewBox = "0 0 120 120" fill = "none" xmlns = "http://www.w3.org/2000/svg" >
< path d = "M23.96,85.24 A44,44 0 0 1 52.36,16.67" stroke = "#1B2A57" stroke-width = "14" stroke-linecap = "round" />
< path d = "M93.70,88.28 A44,44 0 0 1 28.89,91.11" stroke = "#AAD4F5" stroke-width = "14" stroke-linecap = "round" />
< path d = "M67.64,16.67 A44,44 0 0 1 98.10,38.00" stroke = "#F4623A" stroke-width = "14" stroke-linecap = "round" />
< circle cx = "103.04" cy = "50.85" r = "7.7" fill = "#F4623A" />
2026-09-08 14:28:14 +10:00
</ svg >
2026-09-08 15:23:31 +10:00
< span class = "name" > CycleWise< span style = "color:#F4623A" > AU</ span ></ span >
2026-09-08 14:28:14 +10:00
< span class = "tag" > Menstrual & Pregnancy Charting</ span >
</ div >
< nav class = "mode-switch" role = "tablist" aria-label = "Tracking mode" >
2026-10-15 00:28:35 +00:00
< button class = "mode-btn active" id = "tab-period" aria-controls = "mode-period" data-mode = "period" role = "tab" aria-selected = "true" > Period</ button >
< button class = "mode-btn" id = "tab-ovulation" aria-controls = "mode-ovulation" data-mode = "ovulation" role = "tab" aria-selected = "false" > Ovulation</ button >
< button class = "mode-btn" id = "tab-pregnancy" aria-controls = "mode-pregnancy" data-mode = "pregnancy" role = "tab" aria-selected = "false" > Pregnancy</ button >
2026-09-08 14:28:14 +10:00
</ nav >
</ header >
< main >
<!-- ================= PERIOD ================= -->
2026-10-15 00:28:35 +00:00
< section id = "mode-period" class = "mode-panel active" role = "tabpanel" aria-labelledby = "tab-period" >
2026-09-08 14:28:14 +10:00
< div class = "hero" >
< div >
< span class = "eyebrow" style = "color:var(--period)" > 01 — Menstrual Cycle</ span >
< h1 > Chart your cycle, one day at a time.</ h1 >
< p class = "lede" > Log the first day of your last period and your typical cycle length — the calendar plots your period, fertile window, and ovulation day automatically, and updates every time you adjust the numbers.</ p >
< div class = "controls" >
< div class = "field" >
< label for = "p-start" > Last period started</ label >
< input type = "date" id = "p-start" >
</ div >
< div class = "field" >
< label for = "p-cyclelen" > Cycle length (days)</ label >
< input type = "number" id = "p-cyclelen" min = "21" max = "35" value = "28" >
</ div >
< div class = "field" >
< label for = "p-periodlen" > Period length (days)</ label >
< input type = "number" id = "p-periodlen" min = "2" max = "10" value = "5" >
</ div >
</ div >
< p class = "example-note" id = "p-example-note" > Prefilled with an example date — enter your own to personalize the calendar.</ p >
< div class = "stat-row" id = "period-stats" ></ div >
</ div >
< div class = "hero-art" >
< svg viewBox = "0 0 200 200" aria-hidden = "true" >
< circle cx = "100" cy = "100" r = "88" fill = "none" stroke = "var(--line-strong)" stroke-width = "1.5" />
< path id = "phase-arc-menstrual" fill = "none" stroke = "var(--period)" stroke-width = "10" stroke-linecap = "round" />
< path id = "phase-arc-follicular" fill = "none" stroke = "var(--ovulation-soft)" stroke-width = "10" stroke-linecap = "round" />
< path id = "phase-arc-fertile" fill = "none" stroke = "var(--fertile)" stroke-width = "10" stroke-linecap = "round" />
< path id = "phase-arc-luteal" fill = "none" stroke = "var(--pregnancy-soft)" stroke-width = "10" stroke-linecap = "round" />
< circle id = "today-marker" cx = "100" cy = "12" r = "6" fill = "var(--ink)" />
< text x = "100" y = "96" text-anchor = "middle" class = "mono" font-size = "11" fill = "var(--ink-soft)" > CYCLE DAY</ text >
< text id = "cycle-day-big" x = "100" y = "122" text-anchor = "middle" font-family = "Fraunces,serif" font-size = "34" font-weight = "700" fill = "var(--ink)" > 1</ text >
</ svg >
</ div >
</ div >
< div class = "workbench" >
< div class = "cal-card" >
< div class = "cal-head" >
< h3 id = "p-cal-title" > Month</ h3 >
< div class = "cal-nav" >
< button class = "nav-btn" id = "p-prev" aria-label = "Previous month" > ‹ </ button >
< button class = "nav-btn" id = "p-next" aria-label = "Next month" > › </ button >
</ div >
</ div >
< div class = "weekday-row" >< span > S</ span >< span > M</ span >< span > T</ span >< span > W</ span >< span > T</ span >< span > F</ span >< span > S</ span ></ div >
< div class = "day-grid" id = "p-daygrid" ></ div >
< div class = "legend" >
< span >< i style = "background:var(--period-soft)" ></ i > Period</ span >
< span >< i style = "background:var(--fertile-soft)" ></ i > Fertile window</ span >
< span >< i style = "background:var(--ovulation-soft);box-shadow:inset 0 0 0 2px var(--ovulation-strong)" ></ i > Ovulation</ span >
< span >< i style = "background:var(--surface-sunken)" ></ i > Other</ span >
</ div >
</ div >
< div class = "detail-card" id = "p-detail" ></ div >
</ div >
< div class = "ref-heading" >
< span class = "eyebrow" style = "color:var(--period)" > Reference</ span >
< h2 > The four phases</ h2 >
</ div >
< div class = "ref-grid" id = "phase-ref-grid" ></ div >
</ section >
<!-- ================= OVULATION ================= -->
2026-10-15 00:28:35 +00:00
< section id = "mode-ovulation" class = "mode-panel" role = "tabpanel" aria-labelledby = "tab-ovulation" >
2026-09-08 14:28:14 +10:00
< div class = "hero" >
< div >
< span class = "eyebrow" style = "color:var(--ovulation)" > 02 — Fertile Window</ span >
< h1 > Six days a cycle actually matter.</ h1 >
< p class = "lede" > Conception is only possible in the days leading up to and including ovulation. This view uses the same cycle dates to surface your fertile window, plus what basal body temperature and cervical mucus typically do each day around it.</ p >
< div class = "stat-row" id = "ovulation-stats" ></ div >
</ div >
< div class = "hero-art" >
< svg viewBox = "0 0 200 200" aria-hidden = "true" >
< circle cx = "100" cy = "100" r = "86" fill = "none" stroke = "var(--fertile-soft)" stroke-width = "16" />
< circle cx = "100" cy = "100" r = "86" fill = "none" stroke = "var(--ovulation)" stroke-width = "16"
stroke-dasharray = "49 490" stroke-dashoffset = "-405" stroke-linecap = "round" />
< circle cx = "100" cy = "100" r = "54" fill = "var(--surface)" stroke = "var(--line)" />
< text x = "100" y = "96" text-anchor = "middle" class = "mono" font-size = "10" fill = "var(--ink-soft)" > DAY VS.</ text >
< text x = "100" y = "112" text-anchor = "middle" class = "mono" font-size = "10" fill = "var(--ink-soft)" > OVULATION</ text >
</ svg >
</ div >
</ div >
< div class = "ref-heading" style = "padding-bottom:4px;" >
< span class = "eyebrow" style = "color:var(--ovulation)" > Timeline</ span >
< h2 > Day by day around ovulation</ h2 >
</ div >
< div class = "timeline" id = "ov-timeline" ></ div >
< div class = "workbench" >
< div class = "cal-card" >
< div class = "cal-head" >
< h3 id = "o-cal-title" > Month</ h3 >
< div class = "cal-nav" >
< button class = "nav-btn" id = "o-prev" aria-label = "Previous month" > ‹ </ button >
< button class = "nav-btn" id = "o-next" aria-label = "Next month" > › </ button >
</ div >
</ div >
< div class = "weekday-row" >< span > S</ span >< span > M</ span >< span > T</ span >< span > W</ span >< span > T</ span >< span > F</ span >< span > S</ span ></ div >
< div class = "day-grid" id = "o-daygrid" ></ div >
< div class = "legend" >
< span >< i style = "background:var(--fertile-soft)" ></ i > Fertile window</ span >
< span >< i style = "background:var(--ovulation-soft);box-shadow:inset 0 0 0 2px var(--ovulation-strong)" ></ i > Ovulation day</ span >
< span >< i style = "background:var(--period-soft)" ></ i > Period</ span >
</ div >
</ div >
< div class = "detail-card" id = "o-detail" ></ div >
</ div >
</ section >
<!-- ================= PREGNANCY ================= -->
2026-10-15 00:28:35 +00:00
< section id = "mode-pregnancy" class = "mode-panel" role = "tabpanel" aria-labelledby = "tab-pregnancy" >
2026-09-08 14:28:14 +10:00
< div class = "hero" >
< div >
< span class = "eyebrow" style = "color:var(--pregnancy)" > 03 — Forty Weeks</ span >
< h1 > Every week of pregnancy, mapped.</ h1 >
< p class = "lede" > Enter the first day of your last menstrual period (LMP) — pregnancy is dated from there, not from conception. The tracker converts today's date into a gestational week and your estimated due date.</ p >
< div class = "controls" >
< div class = "field" >
< label for = "pr-lmp" > Last menstrual period</ label >
< input type = "date" id = "pr-lmp" >
</ div >
</ div >
< p class = "example-note" id = "pr-example-note" > Prefilled with an example LMP — enter your own for your real week and due date.</ p >
< div class = "stat-row" id = "pregnancy-stats" ></ div >
</ div >
< div class = "hero-art" >
< svg viewBox = "0 0 200 200" aria-hidden = "true" >
< path d = "M20 160 C 60 150, 60 60, 100 40 C 140 20, 160 60, 180 30" fill = "none" stroke = "var(--pregnancy-soft)" stroke-width = "10" stroke-linecap = "round" />
< path id = "pregnancy-progress-path" d = "M20 160 C 60 150, 60 60, 100 40 C 140 20, 160 60, 180 30" fill = "none" stroke = "var(--pregnancy)" stroke-width = "10" stroke-linecap = "round" stroke-dasharray = "230" stroke-dashoffset = "230" />
< circle id = "pregnancy-progress-dot" cx = "20" cy = "160" r = "7" fill = "var(--pregnancy)" />
</ svg >
</ div >
</ div >
< div class = "trimester-track" >
< div class = "trimester-labels" >
< span style = "color:var(--pregnancy)" > Trimester 1 · Weeks 1– 13</ span >
< span style = "color:var(--pregnancy)" > Trimester 2 · Weeks 14– 27</ span >
< span style = "color:var(--pregnancy)" > Trimester 3 · Weeks 28– 40</ span >
</ div >
2026-10-15 00:28:35 +00:00
< input type = "range" class = "week-slider" id = "week-slider" aria-label = "Pregnancy week, 1 to 40" min = "1" max = "40" value = "20" >
2026-09-08 14:28:14 +10:00
< div class = "week-ticks" >< span > Wk 1</ span >< span > Wk 13</ span >< span > Wk 27</ span >< span > Wk 40</ span ></ div >
</ div >
< div class = "week-panel" >
< div class = "size-card" >
< span class = "weeknum" id = "wk-badge" > WEEK 20</ span >
< div class = "blob" id = "wk-blob" ></ div >
< div class = "cmp" id = "wk-compare" > size of a banana</ div >
< div class = "metric" id = "wk-metric" > ≈ 16.5 cm · 300 g</ div >
</ div >
< div class = "week-info" >
< div class = "info-block" >< h5 > Baby's development</ h5 >< p id = "wk-dev" ></ p ></ div >
< div class = "info-block" >< h5 > Your body</ h5 >< p id = "wk-body" ></ p ></ div >
< div class = "info-block" >< h5 > This week's note</ h5 >< p id = "wk-tip" ></ p ></ div >
</ div >
</ div >
< div class = "ref-heading" style = "padding-bottom:4px;" >
< span class = "eyebrow" style = "color:var(--pregnancy)" > Jump to a week</ span >
< h2 > All 40 weeks</ h2 >
</ div >
< div class = "filmstrip" id = "week-filmstrip" ></ div >
< div class = "workbench" style = "grid-template-columns:1fr;" >
< div class = "cal-card" >
< div class = "cal-head" >
< h3 id = "pr-cal-title" > Month</ h3 >
< div class = "cal-nav" >
< button class = "nav-btn" id = "pr-prev" aria-label = "Previous month" > ‹ </ button >
< button class = "nav-btn" id = "pr-next" aria-label = "Next month" > › </ button >
</ div >
</ div >
< div class = "weekday-row" >< span > S</ span >< span > M</ span >< span > T</ span >< span > W</ span >< span > T</ span >< span > F</ span >< span > S</ span ></ div >
< div class = "day-grid" id = "pr-daygrid" ></ div >
< div class = "legend" >
< span >< i style = "background:var(--pregnancy-soft)" ></ i > Each date labeled with its gestational week</ span >
</ div >
</ div >
</ div >
</ section >
</ main >
< footer class = "note" >
2026-09-08 15:23:31 +10:00
CycleWise AU gives general educational estimates based on average cycle math — not medical advice or a diagnosis. Actual timing varies by body. Speak with a clinician for guidance specific to you. Dates you enter are stored only in this browser, on this device.
2026-09-08 14:28:14 +10:00
</ footer >
</ div >
2026-10-15 00:28:35 +00:00
< script src = "app.js" ></ script >
2026-10-15 00:19:40 +00:00
< script src = "vendor/gsap.min.js" ></ script >
< script src = "vendor/ScrollTrigger.min.js" ></ script >
2026-10-15 00:22:53 +00:00
< script src = "ring3d.js" ></ script >
2026-10-15 00:19:40 +00:00
< script src = "animations.js" ></ script >
2026-10-15 00:24:38 +00:00
< script src = "timeline.js" ></ script >
2026-10-15 00:28:35 +00:00
</ body >
</ html >