Property
Designs
Sketch
ADU Report
Property
Designs
Sketch
ADU Report
Sketch a Detached ADU
Send feedback
Help
Demolish Building: click on an existing building you plan to demolish
Revert
Done
Feedback
Cancel
Send








jurisdictionname(X,Y) :- evaluate(getjur(),X) & evaluate(getjurprettyname(),Y)
jurisdictionlink(X,Y,Z) :- evaluate(getjur(),X) & evaluate(getplanningdescriptiontext(),Y) & evaluate(planningdeptlink(),Z)

%Saves
click(save(X)) & ~saved ==> saved
click(save(X)) & saved ==> ~saved

updatesave(0) ==> ~saved
updatesave(1) ==> saved

attribute(save(1),"class",X) :- evaluate(
  if(
    saved,
    "icon-button icon-heart icon-left active save-button button-text1-bold",
    true,
    "icon-button icon-heart icon-left save-button button-text1-bold"
  )
  , X
)

  innerhtml(save(1),X) :- evaluate(
  if(
    saved,
    "Saved",
    true,
    "Save"
  )
  , X
)



%common logic
status(allowed) :- someallowed & someresidence & ~sometalkToPlanning
status(talkToPlanning) :- ~notallowed & sometalkToPlanning
status(notallowed) :- notallowed
somestatus :- status(X)
notallowed :- zoningdoesntallow
notallowed :- noresidence
permittedadu :- someallowed & ~noresidence

somewaitandlisten :- waitandlisten(X)
somecontactPlanning :- contactPlanning(X)
sometalkToPlanning :- talkToPlanning(X)
sometalkToPlanning :- contactPlanning(X)
sometalkToPlanning :- talktoDCP
sometalkToPlanning :- someallowed & somenotallowed
unknownstandards :- someallowed & somenotallowed
%unknownstandards :- splitzoning

%innerhtml(propertyinformation,X) :- renderedinfo(X)

%Capture SF MF case
showstandards :- someallowed & someresidence & ~unknownstandards & ~somecontactPlanning

domoreavailable :- ~status(notallowed)
recheckavailable :- status(X) & ~zoningdoesntallow & ~unknownstandards

zoningdoesntallow :- ~someallowed

%talkToPlanning :- planned_development
%talkToPlanning :- talkToDCP
%talkToPlanning :- someallowed & somenotallowed
planned_development :- zone(Z) & pdzone(Z)

singlefamilyresidence :- hometype(singlefamily)
multifamilyresidence :- hometype(multifamily)
someresidence :- somehometype & ~noresidence
somehometype :- hometype(X)
noresidence :- hometype(none)

%innerhtml(address,X) :- address(X)
%innerhtml(apn,X) :- apn(X)
%innerhtml(zone,X) :- zone(X)

attribute(locationtext,placeholder,X) :- locationvalue(X)

overviewaddlink(X) :- apn(A) & address(B) & evaluate(
  stringappend(
    "/overview/",
    convertbtoa(
      stringappend(
        "&apn=",A,
        "&address=",B,
        "&jurisdiction=",getjur(),
        "&addbuilding=1"
      )
    )
  ), X
)

attribute(backtooverview(D),href,Y) :- apn(A) & address(B) & evaluate(
  stringappend(
    "/overview/",
    convertbtoa(
      stringappend(
        "&apn=",A,
        "&address=",B,
        "&jurisdiction=",getjur()
      )
    )
  ), Y
)

attribute(designlink(X),href,Y) :- apn(A) & address(B) & evaluate(
  stringappend(
    "/design/",
    convertbtoa(
      stringappend(
        "&apn=",A,
        "&address=",B,
        if(
          somehometype,
          stringappend("&residence=",choose(H,hometype(H))),
          true,
          ""
        ),
        "&jurisdiction=",getjur()
      )
    )
  ), Y
)

attribute(sketchlink(X),href,Y) :- apn(A) & address(B) & uuid(P) & evaluate(
  stringappend(
    "/sketch/",
    convertbtoa(
      stringappend(
        "&apn=",A,
        "&address=",B,
        "&project=",P,
        if(
          somehometype,
          stringappend("&residence=",choose(H,hometype(H))),
          true,
          ""
        ),
        "&jurisdiction=",getjur()
      )
    )
  ), Y
)

attribute(reportlink(X),href,Y) :- apn(A) & address(B) & evaluate(
  stringappend(
    "/designstandards/",
    convertbtoa(
      stringappend(
        "&apn=",A,
        "&address=",B,
        if(
          somehometype,
          stringappend("&residence=",choose(H,hometype(H))),
          true,
          ""
        ),
        "&jurisdiction=",getjur()
      )
    )
  ), Y
)

attribute(reportlink(1),class,X) :- evaluate(
  if(
    showstandards,
    "link",
    true,
    "link disabled"
  ), X
)

style(designlink(D),display,X) :- evaluate(
  if (
    design_disabled,
    "none",
    true,
    ""
  ), X
)

attribute(designlink(X),onclick,"return openandcheckdisclaimer()")
attribute(sketchlink(X),onclick,"return true;")
attribute(reportlink(X),onclick,"_fullreport_was_clicked = true; doSomethingOnLogin(()=>{view_general_report(); location = this.getAttribute('href');}); return false;")
attribute(fullreport,onclick,"_fullreport_was_clicked = true; doSomethingOnLogin(()=>{savesketch(); location = this.getAttribute('href');}); return false;")

attribute(designlink(1),class,X) :- evaluate(
  if(
    somestatus & ~notallowed,
    "link designlink",
    true,
    "link designlink disabled"
  ), X
)
attribute(sketchlink(1),class,X) :- evaluate(
  if(
    somestatus & ~notallowed,
    "link active designlink",
    true,
    "link designlink disabled"
  ), X
)

style(knownstandards(X),display,Y) :- evaluate(
  if (
    showstandards,
    "",
    true,
    "none"
  ), Y
)

innerhtml(knownstandards(1),X) :- sketchpagemoreinfooverride(X)
attribute(knownstandards(1),class,Y) :- sketchpagemoreinfooverride(X) & evaluate(
  if(
    true,
    "small-text"
  ), Y
)

attribute(viewstandards(X),href,Y) :- apn(A) & jurisdiction(J) & hometype(H) & evaluate(
  stringappend(
    "/designstandards/", convertbtoa(stringappend("jurisdiction=",J,"&apn=",A,"&residence=",H))
  )
  ,Y
)

attribute(fullreport,hidden,X) :- evaluate(
  if(
    status(allowed),
    "no",
    true,
    "yes"
  ), X
)

attribute(fullreport,href,Y) :- apn(A) & jurisdiction(J) & project(P) & hometype(H) & evaluate(
  stringappend(
    "/designstandards/", convertbtoa(stringappend("sketch=",P,"&jurisdiction=",J,"&apn=",A,"&hometype=",H))
  )
  ,Y
)

%hide domore container at footer if not allowed
style(domorefooter,display,X) :- evaluate(
  if(
    status(allowed),
    "",
    true,
    "none"
  ), X
)

locationvalue(X) :- addresstext(A) & apn(B) & jurisdiction(C) & jurisdictionname(C,N) & evaluate(
  if(
    distinct(A,"-"),
    A,
    true,
    stringappend(B,", ",N)
  ), X
)

addresstext(Y) :- address(X) & evaluate(
  if(
    empty(X),
    "-",
    true,
    capitalizeone(X)
  ), Y
)



splitzoning :- allowedzone(X) & allowedzone(Y) & distinct(X,Y)

style(recheck(X),display,Y) :- evaluate(
  if(
    recheckavailable,
    "",
    true,
    "none"
  ), Y
)

attribute(eligibilitystatus(X),class,Y) :- evaluate(
  if(
    status(allowed),
    "icon-button icon-left icon-allowed-colored eligibility-note",
    status(talkToPlanning),
    "icon-button icon-left icon-warning-colored eligibility-note",
    true,
    "icon-button icon-left icon-notallowed-colored eligibility-note"
  ), Y
)


planningDepartmentLink(L) :- evaluate(
  stringappend(
    linkstyle("Planning Department",planningdeptlink())
  ), L
)
linkstyle(T,L) := stringappend("<a class='link' href='",L,"' target='_blank'>",T,"</a>")

n(1) n(2)

innerhtml(eligibilitystatus(X),Y) :- n(X) & evaluate(
  if(
    status(allowed),
    if (
      splitzoning,
      "Allowed. However, your property has split zoning. Contact the ",choose(L,planningDepartmentLink(L))," to learn what standards apply to your property.",
      somewaitandlisten,
      choose(W,waitandlisten(W)),
      true,
      "Allowed"
    ),
    status(talkToPlanning),
    if(
      somecontactPlanning,
      choose(Y,contactPlanning(Y)),
      true,
      stringappend(
        choose(Y,talkToPlanningMessage(Y)),
        " Contact the ",choose(L,planningDepartmentLink(L))," to learn what standards apply to your property."
      )
    ),
    somenotallowedreason,
    choose(R,notallowedreason(R)),
    zoningdoesntallow,
    "ADUs are only allowed in zoning districts in which residential uses are allowed. Because residential uses are not allowed in your zoning district, an ADU is not allowed here.",
    true,
    "Unfortunately, accessory dwelling units are not allowed on properties without a single-family or multi-family residence."
  ), Y
)

statustext(X) :- evaluate(
  if(
    status(allowed),
    "An Accessory Dwelling Unit is possible here!",
    status(talkToPlanning),
    "Contact the Planning Department to find out what standards apply.",
    true,
    "An Accessory Dwelling Unit is not allowed here."
  ), X
)

statusclass(X) :- evaluate(
  if(
    status(allowed),
    "large-text bold allowed",
    status(talkToPlanning),
    "large-text bold contactplanning",
    true,
    "large-text bold notallowed"
  ), X
)

innerhtml(statustext,X) :- statustext(X)
attribute(statustext,class,X) :- statusclass(X)

style(viewstandards(X),display,Y) :- evaluate(
  if(
    showstandards,
    "",
    true,
    "none"
  ), Y
)
style(allowedaction(X),display,Y) :- evaluate(
  if(
    ~status(notallowed),
    "",
    true,
    "none"
  ), Y
)
style(notallowedaction(X),display,Y) :- evaluate(
  if(
    status(notallowed),
    "",
    true,
    "none"
  ), Y
)

innerhtml(talkToPlanningMessage,X) :- talkToPlanningMessage(X)

talkToPlanningMessage(X) :- evaluate(
  if(
    someallowed & somenotallowed,
    "Your property has split zoning and ADUs may not be allowed in some parts of your property.",
    somecontactPlanning,
    choose(Y,contactPlanning(Y)),
    sometalkToPlanning,
    stringappend(
      "Your property is in ",
      listappend(setofall(P, talkToPlanning(P))),
      "."
    ),
    somewaitandlisten,
    choose(Y,waitandlisten(Y)),
    splitzoning,
    "Your property has split zoning.",
    true,
    ""
  ), X
)

sometalkToPlanningMessage :- talkToPlanningMessage(X) & distinct(X,"")

style(noresidence,display,X) :- evaluate(
  if(
    noresidence,
    "",
    true,
    "none"
  ), X
)
style(talkToPlanning,display,X) :- evaluate(
  if(
    sometalkToPlanningMessage & ~status(notallowed),
    "",
    true,
    "none"
  ), X
)

style(eligibility(X),display,Y) :- evaluate(
  if(
    somestatus,
    "",
    true,
    "none"
  ), Y
)


empty(X) :- matches(X,"^(\s*)$")

select(locationtext,X) & ~empty(X) ==> locationsearch(X)
select(locationtext,X) & locationsearch(Y) ==> ~locationsearch(Y)

value(locationtext,X) :- locationsearch(X)

load ==> tab(general) & detachedadu

click(toggletab(X)) ==> tab(X)
click(toggletab(X)) & tab(Y) ==> ~tab(Y)

click(checkeligibility(X)) ==> checkeligibility
click(checkeligibility(X)) & hometype(Y) ==> ~hometype(Y)
click(backtooverview(X)) ==> ~checkeligibility

showsection(design) :- ~someproject
showsection(detail) :- someproject
someproject :- project(X)


attribute(checkeligibility(1),interactive,X) :- evaluate(
  if(
    somestatus,
    "no",
    true,
    "yes"
  ), X
)
attribute(checkeligibility(1),class,X) :- evaluate(
  if(
    status(notallowed),
    "button-capsule icon-button icon-left active icon-notallowed",
    status(allowed),
    "button-capsule icon-button icon-left active icon-allowed",
    status(talkToPlanning),
    "button-capsule icon-button icon-left active icon-warning",
    true,
    "button-capsule"
  ), X
)

style(header(X),display,Y) :- evaluate(
  if(
    showsection(X),
    "",
    true,
    "none"
  ), Y
)
style(content(X),display,Y) :- evaluate(
  if(
    showsection(X),
    "",
    true,
    "none"
  ), Y
)

attribute(aducheck,"interactive",X) :- evaluate(
  if(
    permittedadu,
    "no",
    notpermittedadu,
    "no",
    true,
    "yes"
  ), X
)
attribute(aducheck,"class",X) :- evaluate(
  if(
    permittedadu,
    "button-capsule active icon-button icon-allowed icon-left",
    notpermittedadu,
    "button-capsule active icon-button icon-notallowed icon-left",
    true,
    "button-capsule"
  ), X
)

style(tab(X),display,Y) :- evaluate(
  if(
    tab(X),
    "",
    true,
    "none"
  ), Y
)

style(editing,display,Y) :- evaluate(
  if(
    tab(general),
    "",
    true,
    "none"
  ), Y
)

attribute(toggletab(X),"class",Y) :- evaluate(
  if(
    tab(X),
    "button-capsule active",
    true,
    "button-capsule"
  ), Y
)

click(editmap) ==> editmap
click(donemap) ==> ~editmap
click(cancelmap) ==> ~editmap

style(editmap,display,X) :- evaluate(
  if (
    editmap,
    "none",
    true,
    ""
  ), X
)

attribute(maplayer,editmode,X) :- evaluate(
  if(
    editmap,
    "yes",
    true,
    "no"
  ), X
)

click(remove) ==> action(remove)
click(reshape) ==> action(reshape)

attribute(maplayer,delete,"yes") :- editmap
attribute(maplayer,delete,"no") :- ~editmap

resetaction & action(X) ==> ~action(X)

resetaction :- click(cancellocal)
resetaction :- click(donelocal)
someaction :- action(X)

attribute(page,class,X) :- evaluate(
  if(
    false,
    "sidebar property-overview full-height shadowed",
    true,
    "sidebar eligibility full-height shadowed"
  ), X
)

style(page,display,X) :- evaluate(
  if(
    ~editmap,
    "",
    true,
    "none"
  ), X
)

style(demolishscreen,display,X) :- evaluate(
  if(
    editmap,
    "",
    true,
    "none"
  ), X
)

innerhtml(aduheight,Y) :- aduheight(X) & beautifynumber(X,Z) & ftstyle(Z,Y)
innerhtml(aduheight,"NA") :- ~someaduheight

innerhtml(aduarea,Y) :- aduarea(X) & sqftstyle(X,Y)
innerhtml(aduarea,"NA") :- ~someaduarea

innerhtml(aduminarea,Y) :- aduminarea(X) & sqftstyle(X,Y)
innerhtml(aduminarea,"NA") :- ~someaduminarea

someaduheight :- aduheight(X)
someaduarea :- aduarea(X)
someaduminarea :- aduminarea(X)


attribute(goback,href,Y) :- apn(A) & jurisdiction(J) & evaluate(
  stringappend(
    "/overview/", convertbtoa(stringappend("jurisdiction=",J,"&apn=",A))
  ), Y
)


innerhtml(sketcharea,Y) :- sketcharea(X) & sqftstyle(X,Y)
sketcharea(X) :- width([WFT,W]) & depth([DFT,D]) & evaluate(quotient(times(W,D),144),X)
actualarea(X) :- width([WFT,W]) & depth([DFT,D]) & evaluate(quotient(times(W,D),144),X)

value(width,X) :- width(X)
value(depth,X) :- depth(X)
value(costpersqft,X) :- computedcostpersqft(X)
value(constructiontype,X) :- constructiontype(X)
value(buildingfinishes,X) :- buildingfinishes(X)

attribute(slider(width),value,X) :- width([Y,X])
attribute(slider(depth),value,X) :- depth([Y,X])
attribute(slider(costpersqft),value,X) :- computedcostpersqft(X)

dimension(width,X) :- width([T,X])
dimension(depth,X) :- depth([T,X])

select(slider(width),X) & evaluate(dimensionformat(X),Y) ==> width([Y,X])
select(slider(width),X) & width(Y) ==> ~width(Y)

select(slider(depth),X) & evaluate(dimensionformat(X),Y)==> depth([Y,X])
select(slider(depth),X) & depth(Y) ==> ~depth(Y)

select(width,X) & validdimension(X) ==> width(X)
select(width,X) & validdimension(X) & width(Y) ==> ~width(Y)
select(depth,X) & validdimension(X) ==> depth(X)
select(depth,X) & validdimension(X) & depth(Y) ==> ~depth(Y)

select(constructiontype,X) ==> constructiontype(X)
select(constructiontype,X) & constructiontype(Y) ==> ~constructiontype(Y)

select(buildingfinishes,X) ==> buildingfinishes(X)
select(buildingfinishes,X) & buildingfinishes(Y) ==> ~buildingfinishes(Y)

select(constructiontype,X) & costpersqft(Y) ==> ~costpersqft(Y)
select(buildingfinishes,X) & costpersqft(Y) ==> ~costpersqft(Y)

select(slider(costpersqft),X) ==> costpersqft(X)
select(slider(costpersqft),X) & costpersqft(Y) ==> ~costpersqft(Y)

select(costpersqft,X) & valid_constructioncost(X) ==> costpersqft(X)
select(costpersqft,X) & costpersqft(Y) ==> ~costpersqft(Y)

%Between 5 and 150 ft
validdimension([TXT,DIM]) :- ge(DIM,60) & le(DIM,960)
valid_constructioncost(X) :- ge(X,200) & le(X,960)

innerhtml(costrange,X) :- costrange(High,Low) & evaluate(
  stringappend(
    "$",
    beautifynumber(truncateNumber(Low,3)),
    " - $",
    beautifynumber(truncateNumber(High,3))
  ), X
)

truncateNumber(N,P) := times(floor(quotient(N,pow(10,P))),pow(10,P))

computedcostpersqft(X) :- defaultcostloaded & evaluate(
  if(
    somecostpersqft,
    choose(Y,costpersqft(Y)),
    true,
    choose(Y,costperaveragesqftfromdata(Y))
  ), X
)

costrange(High,Low) :- defaultcostloaded & evaluate(
  if(
    somecostpersqft,
    choose([X,Y], userrange(X,Y)),
    true,
    choose([X,Y], defaultrange(X,Y))
  ), [High,Low]
)

userrange(HighArea,LowArea) :- sketcharea(Y) & costpersqft(Z) & evaluate(times(Y,Z),Cost) & defaultvariation(Delta) & evaluate(max(plus(Cost,Delta),85000),HighArea) & evaluate(max(minus(Cost,Delta),75000),LowArea)

defaultvariation(CostDelta) :- sketcharea(Area) & costpersqftfromdata(High,Low) & evaluate(times(Area,quotient(minus(High,Low),2)),CostDelta)

defaultrange(HighArea,LowArea) :- costpersqftfromdata(High,Low) & sketcharea(Area) & evaluate(max(times(High,Area),110000),HighArea) & evaluate(max(times(Low,Area),100000),LowArea)

costperaveragesqftfromdata(X) :- costpersqftfromdata(High,Low) & evaluate(round(quotient(plus(High,Low),2)),X)

costpersqftfromdata(High,Low) :- defaultcostpersqft(
  High_site_standard,Low_site_standard,
  High_prefab_standard,Low_prefab_standard,
  High_site_premium,Low_site_premium,
  High_prefab_premium,Low_prefab_premium
) & evaluate(
  if(
    buildingfinishes(standard),
    if (
      constructiontype(sitebuilt),
      [High_site_standard,Low_site_standard],
      true,
      [High_prefab_standard,Low_prefab_standard]
    ),
    true,
    if (
      constructiontype(sitebuilt),
      [High_site_premium,Low_site_premium],
      true,
      [High_prefab_premium,Low_prefab_premium]
    )
  ), [High,Low]
)
defaultcostloaded :- costpersqftfromdata(High,Low)
somecostpersqft :- costpersqft(X)

innerhtml(statustitle,X) :- evaluate(
  if(
    status(allowed),
    "Analysis",
    true,
    "<div class='inline-icon status-icon icon-error'></div>"
  ), X
)

shouldMapClickBeHonored :- ~editmap

%If there is an address, use everything before the ","| If just APN, then use APN + Jur
innerhtml(title,X) :- locationvalue(K) & addresstext(I) & evaluate(
  stringappend(
    "Sketch | ",
    if(
    matches(I,"-"),
    K,
    true,
    choose(V,matches(K,"^([^,]*),.*$",V))
    ),
    " | Symbium Build"
  ), X
)